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.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);
|
||||||
|
|||||||
+51
-26
@@ -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) {
|
||||||
|
if (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
const loadInfo = {sourceUrl: this.fileDisplayName};
|
const loadInfo = {sourceUrl: this.fileDisplayName};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user