diff --git a/src/css/main.css b/src/css/main.css index 0c8b64e..3da5f60 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -51,6 +51,16 @@ output { display: none; } +/* Bag Info */ + +.bag-info :invalid { + border-color: red; +} + +.bag-info td:last-child { + width: 50px; +} + /* File Selection */ .dropzone { diff --git a/src/index.html b/src/index.html index 0a447c9..98bad07 100644 --- a/src/index.html +++ b/src/index.html @@ -131,7 +131,102 @@

Create a bag

-
Bag Info coming soon
+
+ + + + + + + + + + + + + + + + + + + + + + +
+

Bag Info

+
LabelValue
+ + + +
+ +
+
diff --git a/src/js/BagInfo.js b/src/js/BagInfo.js new file mode 100644 index 0000000..09b44ea --- /dev/null +++ b/src/js/BagInfo.js @@ -0,0 +1,67 @@ +/* + UI for editing bag-info.txt values +*/ + +export default class BagInfo { + constructor(container) { + this.container = container; + + this.elementBody = container.querySelector("tbody"); + + this.elementTemplate = container.querySelector( + "template#bag-info-element-template" + ); + + this.addElement( + "Bagging-Date", + new Date().toISOString().substring(0, 10) + ); + + container + .querySelector(".add-element .dropdown-menu") + .addEventListener("click", evt => { + if ("label" in evt.target.dataset) { + this.addElement(evt.target.dataset.label); + return false; + } + }); + + this.elementBody.addEventListener("click", evt => { + if (evt.target.classList.contains("delete-row")) { + evt.target.parentNode.parentNode.remove(); + } + }); + } + + addElement(label, value) { + label = (label || "").trim(); + value = (value || "").trim(); + + let entryTemplate = document.importNode( + this.elementTemplate.content, + true + ); + + entryTemplate.querySelector(".bag-info-label").value = label; + entryTemplate.querySelector(".bag-info-value").value = value; + + this.elementBody.appendChild(entryTemplate); + } + + getValues() { + // This is not a Map because bag-info.txt allows multiple values for the same label and we must preserve ordering: + let info = []; + this.container.querySelectorAll("tbody tr").forEach(row => { + let labelInput = row.querySelector( + "input.bag-info-label:not(:invalid)" + ); + let valueInput = row.querySelector( + "input.bag-info-value:not(:invalid)" + ); + if (labelInput && valueInput) { + info.push([labelInput.value, valueInput.value]); + } + }); + return info; + } +} diff --git a/src/js/Bagger.js b/src/js/Bagger.js index c45d395..4d35511 100644 --- a/src/js/Bagger.js +++ b/src/js/Bagger.js @@ -2,6 +2,7 @@ import {$} from "./utils.js"; import BagEntry from "./BagEntry.js"; +import BagInfo from "./BagInfo.js"; import Dashboard from "./Dashboard.js"; import SelectFiles from "./SelectFiles.js"; import StorageManager from "./StorageManager.js"; @@ -15,6 +16,8 @@ export default class Bagger { // manifests after a successful upload: this.bagEntries = new Map(); + this.bagInfo = new BagInfo($(".bag-info", elem)); + this.dashboard = new Dashboard($(".dashboard", elem)); this.storage = new StorageManager($(".server-info", elem), status => { @@ -389,10 +392,13 @@ export default class Bagger { ); } - // FIXME: Bag Info UI — https://github.com/LibraryOfCongress/bagger-js/issues/13 let bagInfo = "Bag-Size: " + filesize(totalBytes, {round: 1}); bagInfo += "\nPayload-Oxum: " + totalBytes + "." + totalFiles + "\n"; + this.bagInfo.getValues().forEach(([label, value]) => { + bagInfo += `${label}: ${value}\n`; + }); + let bagIt = "BagIt-Version: 1.0\nTag-File-Character-Encoding: UTF-8\n"; // FIXME: implement tag manifests!