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:
+65
-1
@@ -16,6 +16,7 @@ class ReplayWebApp extends LitElement
|
||||
this.sourceUrl = null;
|
||||
this.collTitle = null;
|
||||
this.showAbout = false;
|
||||
this.showFileDropOverlay = false;
|
||||
this.pageParams = new URLSearchParams();
|
||||
|
||||
this.inited = false;
|
||||
@@ -31,6 +32,37 @@ class ReplayWebApp extends LitElement
|
||||
this.swmanager.register().catch(() => this.swErrorMsg = this.swmanager.renderErrorReport(this.mainLogo));
|
||||
|
||||
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() {
|
||||
@@ -48,6 +80,7 @@ class ReplayWebApp extends LitElement
|
||||
sourceUrl: { type: String },
|
||||
navMenuShown: { type: Boolean },
|
||||
showAbout: { type: Boolean },
|
||||
showFileDropOverlay: { type: Boolean },
|
||||
collTitle: { type: String },
|
||||
loadInfo: { type: Object },
|
||||
embed: { type: String },
|
||||
@@ -112,6 +145,29 @@ class ReplayWebApp extends LitElement
|
||||
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) {
|
||||
.menu-only {
|
||||
display: none;
|
||||
@@ -239,7 +295,7 @@ class ReplayWebApp extends LitElement
|
||||
${!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>
|
||||
` : ""}
|
||||
<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>`;
|
||||
}
|
||||
|
||||
@@ -258,6 +314,8 @@ class ReplayWebApp extends LitElement
|
||||
${this.sourceUrl ? this.renderColl() : this.renderHomeIndex()}
|
||||
|
||||
${this.showAbout ? this.renderAbout() : ""}
|
||||
|
||||
${this.showFileDropOverlay ? this.renderDropFileOverlay() : ""}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -536,6 +594,12 @@ class ReplayWebApp extends LitElement
|
||||
onAboutClose() {
|
||||
this.showAbout = false;
|
||||
}
|
||||
|
||||
renderDropFileOverlay() {
|
||||
return html`
|
||||
<div class="drop-file-overlay">Drop to load web archive</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("replay-app-main", ReplayWebApp);
|
||||
|
||||
+51
-26
@@ -12,38 +12,15 @@ class Chooser extends LitElement
|
||||
|
||||
this.fileDisplayName = "";
|
||||
this.file = null;
|
||||
this.droppedFile = null;
|
||||
|
||||
this.hasNativeFS = !!window.showOpenFilePicker && !IS_APP;
|
||||
|
||||
this.newFullImport = false;
|
||||
|
||||
this.noHead = false;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
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 = {
|
||||
this.showOpenFilePickerOptions = {
|
||||
types: [
|
||||
{
|
||||
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.file = await fileHandle.getFile();
|
||||
@@ -70,7 +93,9 @@ class Chooser extends LitElement
|
||||
}
|
||||
|
||||
onStartLoad(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
const loadInfo = {sourceUrl: this.fileDisplayName};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user