Support dropping files onto window to load (#163)

- Allow dropping a supported file type onto the window to automatically
  load it as an alternative to clicking the "Choose File..." button,
  selecting a file in the file dialog, and clicking the "Load" button.
- Do not do anything when unsupported files are dropped.
This commit is contained in:
Curt Grimes
2023-03-31 18:47:00 -07:00
committed by GitHub
parent 913a28b594
commit d4832e4df9
3 changed files with 168 additions and 52 deletions
+65 -1
View File
@@ -16,6 +16,7 @@ class ReplayWebApp extends LitElement
this.sourceUrl = null; this.sourceUrl = null;
this.collTitle = null; this.collTitle = null;
this.showAbout = false; this.showAbout = false;
this.showFileDropOverlay = false;
this.pageParams = new URLSearchParams(); this.pageParams = new URLSearchParams();
this.inited = false; this.inited = false;
@@ -31,6 +32,37 @@ class ReplayWebApp extends LitElement
this.swmanager.register().catch(() => this.swErrorMsg = this.swmanager.renderErrorReport(this.mainLogo)); this.swmanager.register().catch(() => this.swErrorMsg = this.swmanager.renderErrorReport(this.mainLogo));
this.safariKeyframes(); this.safariKeyframes();
this.addEventListener("dragenter", (dragEvent) => {
this.maybeStartFileDrop(dragEvent);
});
this.addEventListener("dragover", (dragEvent) => {
this.maybeStartFileDrop(dragEvent);
});
this.addEventListener("dragleave", () => {
this.showFileDropOverlay = false;
});
this.addEventListener("dragend", () => {
this.showFileDropOverlay = false;
});
this.addEventListener("drop", (dragEvent) => {
this.droppedFile = dragEvent.dataTransfer.files[0];
this.showFileDropOverlay = false;
dragEvent.preventDefault();
});
this.maybeStartFileDrop = (dragEvent) => {
if (this.sourceUrl) {
// A source is already loaded. Don't allow dropping a file.
return;
}
this.showFileDropOverlay = true;
dragEvent.preventDefault();
};
} }
get appName() { get appName() {
@@ -48,6 +80,7 @@ class ReplayWebApp extends LitElement
sourceUrl: { type: String }, sourceUrl: { type: String },
navMenuShown: { type: Boolean }, navMenuShown: { type: Boolean },
showAbout: { type: Boolean }, showAbout: { type: Boolean },
showFileDropOverlay: { type: Boolean },
collTitle: { type: String }, collTitle: { type: String },
loadInfo: { type: Object }, loadInfo: { type: Object },
embed: { type: String }, embed: { type: String },
@@ -112,6 +145,29 @@ class ReplayWebApp extends LitElement
margin-top: 1.0rem; margin-top: 1.0rem;
} }
.drop-file-overlay {
position: relative;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
inset: 0;
z-index: 50;
font-weight: bold;
font-size: 1.5rem;
background: rgba(255,255,255,.5);
backdrop-filter: blur(2px);
}
.drop-file-overlay:after {
pointer-events: none;
content: " ";
position: absolute;
inset: 0;
border: 5px dashed #aaa;
margin: 15px;
}
@media screen and (min-width: 840px) { @media screen and (min-width: 840px) {
.menu-only { .menu-only {
display: none; display: none;
@@ -239,7 +295,7 @@ class ReplayWebApp extends LitElement
${!IS_APP ? html` ${!IS_APP ? html`
<p slot="header" class="tagline is-size-5 has-text-centered">Explore and Replay Interactive Archived Webpages Directly in your Browser. <i><a target="_blank" href="./docs/examples">(See Examples)</a></i></p> <p slot="header" class="tagline is-size-5 has-text-centered">Explore and Replay Interactive Archived Webpages Directly in your Browser. <i><a target="_blank" href="./docs/examples">(See Examples)</a></i></p>
` : ""} ` : ""}
<wr-chooser slot="header" @load-start=${this.onStartLoad}></wr-chooser> <wr-chooser slot="header" .droppedFile=${this.droppedFile} @did-drop-file="${() => this.droppedFile = null}" @load-start=${this.onStartLoad}></wr-chooser>
</wr-coll-index>`; </wr-coll-index>`;
} }
@@ -258,6 +314,8 @@ class ReplayWebApp extends LitElement
${this.sourceUrl ? this.renderColl() : this.renderHomeIndex()} ${this.sourceUrl ? this.renderColl() : this.renderHomeIndex()}
${this.showAbout ? this.renderAbout() : ""} ${this.showAbout ? this.renderAbout() : ""}
${this.showFileDropOverlay ? this.renderDropFileOverlay() : ""}
`; `;
} }
@@ -536,6 +594,12 @@ class ReplayWebApp extends LitElement
onAboutClose() { onAboutClose() {
this.showAbout = false; this.showAbout = false;
} }
renderDropFileOverlay() {
return html`
<div class="drop-file-overlay">Drop to load web archive</div>
`;
}
} }
customElements.define("replay-app-main", ReplayWebApp); customElements.define("replay-app-main", ReplayWebApp);
+52 -27
View File
@@ -12,38 +12,15 @@ class Chooser extends LitElement
this.fileDisplayName = ""; this.fileDisplayName = "";
this.file = null; this.file = null;
this.droppedFile = null;
this.hasNativeFS = !!window.showOpenFilePicker && !IS_APP; this.hasNativeFS = !!window.showOpenFilePicker && !IS_APP;
this.newFullImport = false; this.newFullImport = false;
this.noHead = false; this.noHead = false;
}
static get properties() { this.showOpenFilePickerOptions = {
return {
fileDisplayName: { type: String },
newFullImport: { type: Boolean },
noHead: { type: Boolean }
};
}
onChooseFile(event) {
if (event.currentTarget.files.length === 0) {
return;
}
this.file = event.currentTarget.files[0];
// file.path only available in electron app
this.fileDisplayName = "file://" + (this.file.path || this.file.name);
}
async onChooseNativeFile() {
if (!this.hasNativeFS) {
return;
}
const options = {
types: [ types: [
{ {
description: "WARC, WACZ, HAR and WBN Files", description: "WARC, WACZ, HAR and WBN Files",
@@ -57,8 +34,54 @@ class Chooser extends LitElement
} }
] ]
}; };
}
const [fileHandle] = await window.showOpenFilePicker(options); static get properties() {
return {
fileDisplayName: { type: String },
droppedFile: { type: File },
newFullImport: { type: Boolean },
noHead: { type: Boolean }
};
}
updated(changedProperties) {
if (changedProperties.has("droppedFile") && this.droppedFile) {
this.onDropFile();
}
}
onDropFile() {
const allowedFileExtensions = this.showOpenFilePickerOptions.types.map(type => type.accept).map(Object.values).flat(2);
const fileHasAllowedExtension = allowedFileExtensions.some(extension => this.droppedFile.name.endsWith(extension));
if (fileHasAllowedExtension) {
this.setFile(this.droppedFile);
this.dispatchEvent(new CustomEvent("did-drop-file", { bubbles: true, composed: true }));
this.onStartLoad(); // Automatically load the file
}
}
onChooseFile(event) {
if (event.currentTarget.files.length === 0) {
return;
}
this.setFile(event.currentTarget.files[0]);
}
setFile(file) {
this.file = file;
// file.path only available in electron app
this.fileDisplayName = "file://" + (this.file.path || this.file.name);
}
async onChooseNativeFile() {
if (!this.hasNativeFS) {
return;
}
const [fileHandle] = await window.showOpenFilePicker(this.showOpenFilePickerOptions);
this.fileHandle = fileHandle; this.fileHandle = fileHandle;
this.file = await fileHandle.getFile(); this.file = await fileHandle.getFile();
@@ -70,7 +93,9 @@ class Chooser extends LitElement
} }
onStartLoad(event) { onStartLoad(event) {
event.preventDefault(); if (event) {
event.preventDefault();
}
const loadInfo = {sourceUrl: this.fileDisplayName}; const loadInfo = {sourceUrl: this.fileDisplayName};
+51 -24
View File
File diff suppressed because one or more lines are too long