ui refactoring on search views:

- fix flexbox sizing
- update icons
- url-resources view: make responsive, use wr-sorter
- pages: various tweaks, hide sidebar for better responsive view!
- app: hide nav buttons when in archive view to avoid redundant
- add page view button to context menu
This commit is contained in:
Ilya Kreymer
2020-08-05 20:05:12 -07:00
parent 6b343e7e65
commit 849f8ff4eb
9 changed files with 2115 additions and 1248 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "replaywebpage", "name": "replaywebpage",
"productName": "ReplayWeb.page", "productName": "ReplayWeb.page",
"version": "1.0.2", "version": "1.1.0-beta.0",
"description": "Serverless Web Archive Replay", "description": "Serverless Web Archive Replay",
"repository": "https://github.com/webrecorder/replayweb.page", "repository": "https://github.com/webrecorder/replayweb.page",
"homepage": "https://replayweb.page/", "homepage": "https://replayweb.page/",
@@ -21,7 +21,7 @@
}, },
"devDependencies": { "devDependencies": {
"css-loader": "^3.5.3", "css-loader": "^3.5.3",
"electron": "^9.0.5", "electron": "9.1.2",
"electron-builder": "^22.7.0", "electron-builder": "^22.7.0",
"electron-notarize": "^1.0.0", "electron-notarize": "^1.0.0",
"http-server": "^0.12.3", "http-server": "^0.12.3",
+1 -1
View File
@@ -139,7 +139,7 @@ class App extends LitElement
<span class="has-text-link">web.page</span> <span class="has-text-link">web.page</span>
</a>` : ``} </a>` : ``}
${IS_APP ? html` ${IS_APP && !this.collInfo ? html`
<a class="navbar-item arrow-button" title="Go Back" @click="${(e) => window.history.back()}"> <a class="navbar-item arrow-button" title="Go Back" @click="${(e) => window.history.back()}">
<fa-icon .svg="${fasArrowLeft}"></fa-icon><span class="menu-only">&nbsp;Go Back</span> <fa-icon .svg="${fasArrowLeft}"></fa-icon><span class="menu-only">&nbsp;Go Back</span>
</a> </a>
+33 -17
View File
@@ -6,10 +6,8 @@ import { sourceToId } from './pageutils';
import fasBook from '@fortawesome/fontawesome-free/svgs/solid/book.svg'; import fasBook from '@fortawesome/fontawesome-free/svgs/solid/book.svg';
import farListAlt from '@fortawesome/fontawesome-free/svgs/regular/list-alt.svg'; import farListAlt from '@fortawesome/fontawesome-free/svgs/regular/list-alt.svg';
import farResources from '@fortawesome/fontawesome-free/svgs/regular/file-code.svg'; import farResources from '@fortawesome/fontawesome-free/svgs/solid/puzzle-piece.svg';
import farPages from '@fortawesome/fontawesome-free/svgs/regular/file-image.svg'; import farPages from '@fortawesome/fontawesome-free/svgs/regular/file-image.svg';
import farPlayCircle from '@fortawesome/fontawesome-free/svgs/regular/play-circle.svg';
import { tsToDate } from './pageutils'; import { tsToDate } from './pageutils';
@@ -57,6 +55,8 @@ class Coll extends LitElement
this.menuActive = false; this.menuActive = false;
this.hasStory = false; this.hasStory = false;
this.editable = false;
} }
static get properties() { static get properties() {
@@ -325,6 +325,11 @@ class Coll extends LitElement
display: none; display: none;
} }
.grey-disabled {
--fa-icon-fill-color: lightgrey;
color: lightgrey;
}
.replay-bar .button:focus { .replay-bar .button:focus {
box-shadow: none; box-shadow: none;
} }
@@ -402,32 +407,34 @@ class Coll extends LitElement
renderLocationBar() { renderLocationBar() {
const dateStr = tsToDate(this.ts).toLocaleString(); const dateStr = tsToDate(this.ts).toLocaleString();
const isReplay = this.tabData.view === "replay";
return html` return html`
<div class="replay-bar"> <div class="replay-bar">
<div class="field has-addons"> <div class="field has-addons">
<button id="fullscreen" class="button is-borderless is-hidden-mobile" @click="${this.onFullscreenToggle}"> <button id="fullscreen" class="button is-borderless is-hidden-touch" @click="${this.onFullscreenToggle}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${this.isFullscreen ? fasUnfullscreen : fasFullscreen}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${this.isFullscreen ? fasUnfullscreen : fasFullscreen}"></fa-icon>
</span> </span>
</button> </button>
<button class="button is-borderless is-hidden-touch" @click="${this.onGoBack}"> <button class="button is-borderless is-hidden-mobile" @click="${this.onGoBack}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${fasLeft}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${fasLeft}"></fa-icon>
</span> </span>
</button> </button>
<button class="button is-borderless is-hidden-touch" @click="${this.onGoForward}"> <button class="button is-borderless is-hidden-mobile" @click="${this.onGoForward}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${fasRight}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${fasRight}"></fa-icon>
</span> </span>
</button> </button>
<button id="refresh" class="button is-borderless ${this.isLoading ? 'is-loading' : 'is-hidden-mobile'}" @click="${this.onRefresh}"> <button id="refresh" class="button is-borderless ${!isReplay ? 'grey-disabled' : ''} ${this.isLoading ? 'is-loading' : 'is-hidden-mobile'}" @click="${this.onRefresh}">
<span class="icon is-small"> <span class="icon is-small">
${!this.isLoading ? html` ${!this.isLoading ? html`
<fa-icon size="1.0em" class="has-text-grey" .svg="${fasRefresh}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${fasRefresh}"></fa-icon>
` : ``} ` : ``}
</span> </span>
</button> </button>
<button class="button is-borderless is-hidden-touch" @click="${this.onGoPages}"> <button class="button is-borderless is-hidden-touch ${!isReplay ? 'grey-disabled' : ''}" @click="${this.onGoPages}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${farListAlt}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${farListAlt}"></fa-icon>
</span> </span>
@@ -435,13 +442,13 @@ class Coll extends LitElement
<form @submit="${this.onSubmit}"> <form @submit="${this.onSubmit}">
<div class="control is-expanded"> <div class="control is-expanded">
<input id="url" class="input" type="text" @keydown="${this.onKeyDown}" .value="${this.url}" placeholder="https://... Enter a URL to replay from the archive here"/> <input id="url" class="input" type="text" @keydown="${this.onKeyDown}" .value="${this.url}" placeholder="https://... Enter a URL to replay from the archive here"/>
${this.tabData.view === "replay" ? html`<p id="datetime" class="control is-hidden-mobile">${dateStr}</p>` : html``} ${isReplay ? html`<p id="datetime" class="control is-hidden-mobile">${dateStr}</p>` : html``}
</div> </div>
</form> </form>
<div class="dropdown is-right ${this.menuActive ? 'is-active' : ''}" @click="${(e) => this.menuActive = false}"> <div class="dropdown is-right ${this.menuActive ? 'is-active' : ''}" @click="${(e) => this.menuActive = false}">
<div class="dropdown-trigger"> <div class="dropdown-trigger">
<button class="${this.embed ? '' : 'is-hidden-tablet'} button is-borderless" aria-haspopup="true" aria-controls="menu-dropdown" @click="${this.onMenu}"> <button class="button is-borderless" aria-haspopup="true" aria-controls="menu-dropdown" @click="${this.onMenu}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${fasMenuV}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${fasMenuV}"></fa-icon>
</span> </span>
@@ -449,30 +456,36 @@ class Coll extends LitElement
</div> </div>
<div class="dropdown-menu" id="menu-dropdown" role="menu"> <div class="dropdown-menu" id="menu-dropdown" role="menu">
<div class="dropdown-content"> <div class="dropdown-content">
<a class="dropdown-item is-hidden-tablet" @click="${this.onFullscreenToggle}"> <a class="dropdown-item is-hidden-desktop" @click="${this.onFullscreenToggle}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${this.isFullscreen ? fasUnfullscreen : fasFullscreen}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${this.isFullscreen ? fasUnfullscreen : fasFullscreen}"></fa-icon>
</span> </span>
<span>Full Screen</span> <span>Full Screen</span>
</a> </a>
<a class="dropdown-item is-hidden-desktop" @click="${this.onGoBack}"> <a class="dropdown-item is-hidden-tablet" @click="${this.onGoBack}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${fasLeft}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${fasLeft}"></fa-icon>
</span> </span>
<span>Back</span> <span>Back</span>
</a> </a>
<a class="dropdown-item is-hidden-desktop" @click="${this.onGoForward}"> <a class="dropdown-item is-hidden-tablet" @click="${this.onGoForward}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${fasRight}"></fa-icon> <fa-icon size="1.0em" class="has-text-grey" .svg="${fasRight}"></fa-icon>
</span> </span>
<span>Forward</span> <span>Forward</span>
</a> </a>
<a class="dropdown-item is-hidden-tablet" @click="${this.onRefresh}"> <a class="dropdown-item is-hidden-tablet ${!isReplay ? 'grey-disabled' : 'has-text-grey'}" @click="${this.onRefresh}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="has-text-grey" .svg="${fasRefresh}"></fa-icon> <fa-icon size="1.0em" class="" .svg="${fasRefresh}"></fa-icon>
</span> </span>
<span>Reload</span> <span>Reload</span>
</a> </a>
<a class="dropdown-item is-hidden-desktop ${!isReplay ? 'grey-disabled' : 'has-text-grey'}" @click="${this.onGoPages}">
<span class="icon is-small">
<fa-icon size="1.0em" class="" .svg="${farListAlt}"></fa-icon>
</span>
<span>Page Search</span>
</a>
<hr class="dropdown-divider is-hidden-desktop"> <hr class="dropdown-divider is-hidden-desktop">
<a class="dropdown-item" @click="${this.onPurgeCache}"> <a class="dropdown-item" @click="${this.onPurgeCache}">
Purge Cache + Full Reload Purge Cache + Full Reload
@@ -581,8 +594,11 @@ class Coll extends LitElement
window.history.forward(); window.history.forward();
} }
onGoPages() { onGoPages(event) {
this.updateTabData({view: "pages"}); event.preventDefault();
if (this.tabData && this.tabData.view === "replay") {
this.updateTabData({view: "pages"});
}
} }
onReAuthed(event) { onReAuthed(event) {
+194 -92
View File
@@ -35,6 +35,9 @@ class Pages extends LitElement
this.selectedPages = new Set(); this.selectedPages = new Set();
this.menuActive = false; this.menuActive = false;
this.sortKey = "ts";
this.sortDesc = true;
} }
static get sortKeys() { static get sortKeys() {
@@ -67,6 +70,9 @@ class Pages extends LitElement
allSelected: { type: Boolean }, allSelected: { type: Boolean },
menuActive: {type: Boolean }, menuActive: {type: Boolean },
sortKey: { type: String },
sortDesc: { type: Boolean }
} }
} }
@@ -167,13 +173,25 @@ class Pages extends LitElement
flex-direction: column; flex-direction: column;
} }
.columns { .main.columns {
width: 100%; width: 100%;
justify-self: stretch; justify-self: stretch;
margin: 1.0em 0 0 0; margin: 1.0em 0 0 0;
min-height: 0px; min-height: 0px;
} }
.header.columns {
width: 100%;
margin-bottom: 0px;
}
.header a {
color: black;
}
.header .column.pagetitle {
margin-left: 2.5em;
}
.column.main-content { .column.main-content {
min-height: 0px; min-height: 0px;
display: flex; display: flex;
@@ -183,27 +201,73 @@ class Pages extends LitElement
margin-left: 0.75em; margin-left: 0.75em;
} }
.sidebar {
display: flex;
flex-direction: column;
border-right: 3px solid rgb(237, 237, 237);
background-color: whitesmoke;
padding-right: 0px;
}
.sidebar-title {
font-size: 1.25rem;
text-transform: uppercase;
margin-bottom: 1.0rem;
word-break: break-word;
}
.sidebar-status {
display: flex;
flex-direction: row;
margin-bottom: 0.5rem;
padding-right: 0.75em;
}
.sidebar-menu {
margin-top: 1.0rem;
}
#filter-label {
margin-bottom: 0px;
}
.num-results {
position: absolute;
right: 16px;
font-style: italic;
font-weight: normal;
}
.asc:after {
content: "▼";
font-size: 0.75em;
}
.desc:after {
content: "▲";
font-size: 0.75em;
}
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.columns { .main.columns {
max-height: 100%; max-height: 100%;
height: 100%; height: 100%;
} }
.column.sidebar { .sidebar-menu {
max-height: 100%; max-height: 100%;
overflow-y: auto; overflow-y: auto;
} }
} }
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {
.columns { .main.columns {
position: relative; position: relative;
max-height: 100%; max-height: 100%;
height: 100%; height: 100%;
} }
.column.sidebar { .sidebar-menu {
max-height: 150px; max-height: 75px;
overflow-y: auto; overflow-y: auto;
margin-top: 0.75em; margin-top: 0.75em;
} }
@@ -214,9 +278,21 @@ class Pages extends LitElement
border-top: 1px solid black; border-top: 1px solid black;
width: 100%; width: 100%;
min-height: 0px;
height: 100%; height: 100%;
max-height: calc(100% - 150px - 0.75em);
padding: 0px; padding: 0px;
margin: 0px;
}
.mobile-header {
margin: 0.5rem;
display: flex;
flex-direction: row;
min-height: 24px;
}
.num-results {
top: 8px;
} }
.menu { .menu {
@@ -229,26 +305,25 @@ class Pages extends LitElement
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: auto; flex: auto;
height: 100%;
margin-bottom: 1.0em; padding-bottom: 1.0em;
min-height: 0px;
} }
.selected { .selected {
background-color: ghostwhite; background-color: ghostwhite;
} }
.panel-block { .page-header {
border: 1px solid rgb(237, 237, 237);
padding: 0.75em 1.25em;
}
.status.level {
width: 100%;
}
nav.panel {
box-shadow: none;
display: flex; display: flex;
flex-direction: column; flex-direction: row;
flex: auto; width: 100%;
height: 100%;
margin-bottom: 1.0em;
border-bottom: 3px solid rgb(237, 237, 237);
}
.check-select {
padding-right: 1.0em;
} }
.search-bar { .search-bar {
@@ -280,97 +355,106 @@ class Pages extends LitElement
</div> </div>
</div> </div>
</div> </div>
${this.renderPageHeader()}
<div class="columns"> <div class="main columns">
${this.collInfo.lists.length ? html` <div class="column sidebar is-one-fifth is-hidden-mobile">
<div class="column sidebar is-one-fifth"> <div class="sidebar-title">${this.collInfo.title}</div>
<aside class="menu">
<p class="menu-label">Filter By List:</p> ${this.editable ? html`
<div class="sidebar-actions">
${this.renderDownloadMenu()}
</div>` : ``}
${this.collInfo.lists.length ? html`
<p id="filter-label" class="menu-label">Filter By List:</p>
<aside class="sidebar-menu menu">
<ul class="menu-list"> <ul class="menu-list">
<li> <li>
<a href="#list-0" data-list="0" class="${currList === 0 ? 'is-active' : ''}" <a href="#list-0" data-list="0" class="${currList === 0 ? 'is-active' : ''}"
@click=${this.onSelectList}>All Pages</a> @click=${this.onSelectList}><i>All Pages</i></a>
<ul class="menu-list">${this.collInfo.lists.map(list => html`
<li>
<a @click=${this.onSelectList} href="#list-${list.id}"
data-list="${list.id}"
class="${currList === list.id ? 'is-active' : ''}">${list.title}</a>
</li>`)}
</ul>
</li> </li>
${this.collInfo.lists.map(list => html`
<li>
<a @click=${this.onSelectList} href="#list-${list.id}"
data-list="${list.id}"
class="${currList === list.id ? 'is-active' : ''}">${list.title}</a>
</li>`)}
</ul> </ul>
</aside> </aside>
</div>` : ``} ` : ``}
</div>
<div class="column main-content"> <div class="column main-content">
${this.renderPages()} ${this.renderPages()}
</div> </div>
</div>`; </div>`;
} }
renderDownloadMenu() {
return html`
<div class="dropdown ${this.menuActive ? 'is-active' : ''}">
<div class="dropdown-trigger">
<button @click="${this.onMenu}" class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu">
<span>Download</span>
<span class="icon is-small">
<fa-icon .svg="${fasAngleDown}"/>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<a @click="${(e) => this.onDownload(e, "wacz", true)}" class="dropdown-item">
Download Selected as WACZ (Web Archive Collection)
</a>
<a @click="${(e) => this.onDownload(e, "warc", true)}" class="dropdown-item">
Download Selected as WARC Only
</a>
<hr class="dropdown-divider">
<a @click="${(e) => this.onDownload(e, "wacz", false)}" class="dropdown-item">
Download All as WACZ (Web Archive Collection)
</a>
<a @click="${(e) => this.onDownload(e, "warc", false)}" class="dropdown-item">
Download All as WARC Only
</a>
</div>
</div>
</div>`;
}
renderPageHeader() { renderPageHeader() {
return html` return html`
<div class="panel-block page-header"> ${this.editable ? html`
<div class="status level is-mobile"> <div class="check-select">
<div class="level-left"> <label class="checkbox">
${this.editable ? html` <input @change=${this.onSelectAll} type="checkbox" .checked="${this.allSelected}">
<div class="check-select level-item"> </label>
<label class="checkbox"> </div>` : html``}
<input @change=${this.onSelectAll} type="checkbox" .checked="${this.allSelected}">
</label>
</div>` : ``}
<span class="num-results level-item">${this.formatResults()}</span> <div class="header columns is-hidden-mobile">
<a @click="${this.onSort}" data-key="title" class="column is-2 ${this.sortKey === "title" ? (this.sortDesc ? "desc" : "asc") : ''}">Date</a>
<a @click="${this.onSort}" data-key="ts" class="column is-8 pagetitle ${this.sortKey === "ts" ? (this.sortDesc ? "desc" : "asc") : ''}">Page Title</a>
</div>
${this.editable ? html` <div class="is-hidden-tablet mobile-header">
<div class="level-item dropdown is-hoverable"> <wr-sorter id="pages"
<div class="dropdown-trigger"> .sortKey="${this.sortKey}"
<button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu"> .sortDesc="${this.sortDesc}"
<span>Download</span> .sortKeys="${Pages.sortKeys}"
<span class="icon is-small"> .data="${this.filteredPages}"
<fa-icon .svg="${fasAngleDown}"/> @sort-changed="${this.onSortChanged}"
</span> class="${this.filteredPages.length ? '' : 'is-hidden'}">
</button> </wr-sorter>
</div> </div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<a @click="${(e) => this.onDownload(e, "wacz", true)}" class="dropdown-item">
Download Selected as WACZ (Web Archive Collection)
</a>
<a @click="${(e) => this.onDownload(e, "warc", true)}" class="dropdown-item">
Download Selected as WARC Only
</a>
<hr class="dropdown-divider">
<a @click="${(e) => this.onDownload(e, "wacz", false)}" class="dropdown-item">
Download All as WACZ (Web Archive Collection)
</a>
<a @click="${(e) => this.onDownload(e, "warc", false)}" class="dropdown-item">
Download All as WARC Only
</a>
</div>
</div>
</div>
` : ``}
</div> <span class="num-results">${this.formatResults()}</span>
`;
${this.currList === 0 ? html`
<div class="level-right">
<wr-sorter id="pages"
defaultKey="ts"
?defaultDesc="true"
.sortKeys="${Pages.sortKeys}"
.data="${this.filteredPages}"
@sort-changed="${(e) => this.sortedPages = e.detail.sortedData}"
class="${this.filteredPages.length ? '' : 'is-hidden'} level-item">
</wr-sorter>` : ``}
</div>
</div>
</div>`;
} }
renderPages() { renderPages() {
//const name = this.currList === 0 ? "All Pages" : this.collInfo.lists[this.currList - 1].title; //const name = this.currList === 0 ? "All Pages" : this.collInfo.lists[this.currList - 1].title;
return html` return html`
<div class="page-header has-text-weight-bold">
${this.renderPageHeader()}
</div>
<div class="scroller" @scroll="${this.onScroll}"> <div class="scroller" @scroll="${this.onScroll}">
${this.sortedPages.map((p) => html` ${this.sortedPages.map((p) => html`
<div class="content ${this.selectedPages.has(p.id) ? 'selected' : ''}"> <div class="content ${this.selectedPages.has(p.id) ? 'selected' : ''}">
@@ -392,6 +476,24 @@ class Pages extends LitElement
} }
} }
onSort(event) {
event.preventDefault();
const key = event.currentTarget.getAttribute("data-key");
if (key === this.sortKey) {
this.sortDesc = !this.sortDesc;
} else {
this.sortDesc = false;
this.sortKey = key;
}
}
onSortChanged(event) {
this.sortedPages = event.detail.sortedData;
this.sortKey = event.detail.sortKey;
this.sortDesc = event.detail.sortDesc;
}
onSelectToggle(event) { onSelectToggle(event) {
const {page, selected} = event.detail; const {page, selected} = event.detail;
if (selected) { if (selected) {
@@ -546,8 +648,8 @@ class PageEntry extends LitElement
.delete { .delete {
position: absolute; position: absolute;
top: 0px; top: 8px;
right: 0px; right: 8px;
} }
.delete:hover { .delete:hover {
+5 -1
View File
@@ -80,7 +80,11 @@ class Sorter extends LitElement
} }
sendSortChanged() { sendSortChanged() {
const detail = {sortedData: this.sortedData.slice(0, this.numResults)}; const detail = {
sortKey: this.sortKey,
sortDesc: this.sortDesc,
sortedData: this.sortedData.slice(0, this.numResults)
};
this.dispatchEvent(new CustomEvent("sort-changed", {detail})); this.dispatchEvent(new CustomEvent("sort-changed", {detail}));
} }
+89 -76
View File
@@ -23,6 +23,27 @@ class URLResources extends LitElement
]; ];
} }
static get sortKeys() {
return [
{
"key": "url",
"name": "URL"
},
{
"key": "ts",
"name": "Date"
},
{
"key": "mime",
"name": "Mime Type",
},
{
"key": "status",
"name": "Status"
}
];
}
constructor() { constructor() {
super(); super();
this.collInfo = null; this.collInfo = null;
@@ -32,6 +53,7 @@ class URLResources extends LitElement
this.urlSearchType = ""; this.urlSearchType = "";
this.filteredResults = []; this.filteredResults = [];
this.sortedResults = [];
this.results = []; this.results = [];
@@ -51,8 +73,8 @@ class URLResources extends LitElement
query: { type: String }, query: { type: String },
urlSearchType: { type: String }, urlSearchType: { type: String },
filteredResults: { type: Array }, filteredResults: { type: Array },
sortedResults: { type: Array },
loading: { type: Boolean }, loading: { type: Boolean },
sortKey: { type: String }, sortKey: { type: String },
sortDesc: { type: Boolean } sortDesc: { type: Boolean }
} }
@@ -174,16 +196,6 @@ class URLResources extends LitElement
} }
this.filteredResults = filteredResults; this.filteredResults = filteredResults;
if (this.sortKey && !(this.sortKey === "url" && !this.sortDesc)) {
this.filteredResults.sort((first, second) => {
if (first[this.sortKey] === second[this.sortKey]) {
return 0;
}
return (this.sortDesc == (first[this.sortKey] < second[this.sortKey])) ? 1 : -1;
});
}
} }
onScroll(event) { onScroll(event) {
@@ -205,58 +217,38 @@ class URLResources extends LitElement
.notification { .notification {
width: 100%; width: 100%;
} }
.main-scroll { .all-results {
/*height: calc(100vh - 180px);*/ margin: 0 0 0 0.5em;
flex-grow: 1;
width: 100vw;
}
table {
table-layout: fixed;
word-wrap: break-word;
text-overflow: ellipsis;
height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 0; min-height: 0;
} }
thead { .main-scroll {
display: table; flex-grow: 1;
} }
tr > th { .minihead {
cursor: pointer; font-size: 10px;
font-weight: bold;
} }
tbody > tr { .columns {
display: table; margin: 0px;
width: 100%;
} }
.col-url { .result {
width: 66%; border-bottom: 1px #dbdbdb solid;
max-width: 66%; }
min-width: 66%; .results-head {
border-bottom: 2px #dbdbdb solid;
margin-right: 16px;
}
.results-head a {
color: black;
}
.all-results .column {
word-break: break-word; word-break: break-word;
} }
.col-ts { div.sort-header {
width: 16%; padding: 10px;
max-width: 16%; margin-bottom: 0px !important;
min-width: 16%;
word-break: break-word;
}
td.col-mime {
width: 10%;
word-break: break-word;
}
td.col-status {
text-align: center;
}
.loading-cog {
width: 100vw;
display: flex;
}
.flex-column {
display: flex;
flex: auto;
max-width: 80%;
flex-direction: column;
} }
.flex-auto { .flex-auto {
flex: auto; flex: auto;
@@ -269,7 +261,10 @@ class URLResources extends LitElement
content: "▲"; content: "▲";
font-size: 0.75em; font-size: 0.75em;
} }
.num-results {
margin-left: 1em;
font-style: italic;
}
`); `);
} }
@@ -302,36 +297,48 @@ class URLResources extends LitElement
<label class="radio has-text-left"><input type="radio" name="urltype" value="" ?checked="${this.urlSearchType === ''}" @click="${this.onClickUrlType}">&nbsp;Contains</label> <label class="radio has-text-left"><input type="radio" name="urltype" value="" ?checked="${this.urlSearchType === ''}" @click="${this.onClickUrlType}">&nbsp;Contains</label>
<label class="radio has-text-left"><input type="radio" name="urltype" value="prefix" ?checked="${this.urlSearchType === 'prefix'}" @click="${this.onClickUrlType}">&nbsp;Prefix</label> <label class="radio has-text-left"><input type="radio" name="urltype" value="prefix" ?checked="${this.urlSearchType === 'prefix'}" @click="${this.onClickUrlType}">&nbsp;Prefix</label>
<label class="radio has-text-left"><input type="radio" name="urltype" value="exact" ?checked="${this.urlSearchType === 'exact'}" @click="${this.onClickUrlType}">&nbsp;Exact</label> <label class="radio has-text-left"><input type="radio" name="urltype" value="exact" ?checked="${this.urlSearchType === 'exact'}" @click="${this.onClickUrlType}">&nbsp;Exact</label>
<span class="is-pulled-right" style="margin-left: 1em">(${this.filteredResults.length} Results)</span> <span class="num-results" is-pulled-right">${this.filteredResults.length} Result(s)</span>
</div> </div>
</div> </div>
</div> </div>
<table class="table is-striped is-fullwidth"> <div class="sort-header is-hidden-tablet">
<thead> <wr-sorter id="urls"
<tr> defaultKey="url"
<th @click="${this.onSort}" data-key="url" class="${this.sortKey === "url" ? (this.sortDesc ? "desc" : "asc") : ''} col-url">URL</th> .sortKey="${this.sortKey}"
<th @click="${this.onSort}" data-key="ts" class="${this.sortKey === "ts" ? (this.sortDesc ? "desc" : "asc") : ''} col-ts">Timestamp</th> .sortDesc="${this.sortDesc}"
<th @click="${this.onSort}" data-key="mime" class="${this.sortKey === "mime" ? (this.sortDesc ? "desc" : "asc") : ''} col-mime">Mime</th> .sortKeys="${URLResources.sortKeys}"
<th @click="${this.onSort}" data-key="status" class="${this.sortKey === "status" ? (this.sortDesc ? "desc" : "asc") : ''} col-status">Status</th> .data="${this.filteredResults}"
</tr> @sort-changed="${this.onSortChanged}">
</thead> </wr-sorter>
<tbody class="main-scroll" @scroll="${this.onScroll}"> </div>
${this.filteredResults.length ?
this.filteredResults.map((result) => html` <div class="all-results">
<tr> <div class="columns results-head has-text-weight-bold">
<td class="col-url"><a @click="${this.onReplay}" data-url="${result.url}" data-ts="${result.ts}" href="#">${result.url}</a></td> <a @click="${this.onSort}" data-key="url" class="column col-url is-6 is-hidden-mobile ${this.sortKey === "url" ? (this.sortDesc ? "desc" : "asc") : ''}">URL</a>
<td class="col-ts">${new Date(result.date).toLocaleString()}</td> <a @click="${this.onSort}" data-key="ts" class="column col-ts is-2 is-hidden-mobile ${this.sortKey === "ts" ? (this.sortDesc ? "desc" : "asc") : ''}">Date</a>
<td class="col-mime">${result.mime}</td> <a @click="${this.onSort}" data-key="mime" class="column col-mime is-3 is-hidden-mobile ${this.sortKey === "mime" ? (this.sortDesc ? "desc" : "asc") : ''}">Mime Type</a>
<td class="col-status">${result.status}</td> <a @click="${this.onSort}" data-key="status" class="column col-status is-1 is-hidden-mobile ${this.sortKey === "status" ? (this.sortDesc ? "desc" : "asc") : ''}">Status</a>
</tr> </div>
<div class="main-scroll" @scroll="${this.onScroll}">
${this.sortedResults.length ?
this.sortedResults.map((result) => html`
<div class="columns result">
<div class="column col-url is-6"><p class="minihead is-hidden-tablet">URL</p><a @click="${this.onReplay}" data-url="${result.url}" data-ts="${result.ts}" href="#">${result.url}</a></div>
<div class="column col-ts is-2"><p class="minihead is-hidden-tablet">Date</p>${new Date(result.date).toLocaleString()}</div>
<div class="column col-mime is-3"><p class="minihead is-hidden-tablet">Mime Type</p>${result.mime}</div>
<div class="column col-status is-1"><p class="minihead is-hidden-tablet">Status</p>${result.status}</div>
</div>
`) : html`<div class="section"><i>No Results Found.</i></div>`} `) : html`<div class="section"><i>No Results Found.</i></div>`}
</tbody> </div>
</table> </div>
`; `;
} }
onSort(event) { onSort(event) {
event.preventDefault();
const key = event.currentTarget.getAttribute("data-key"); const key = event.currentTarget.getAttribute("data-key");
if (key === this.sortKey) { if (key === this.sortKey) {
this.sortDesc = !this.sortDesc; this.sortDesc = !this.sortDesc;
@@ -341,6 +348,12 @@ class URLResources extends LitElement
} }
} }
onSortChanged(event) {
this.sortedResults = event.detail.sortedData;
this.sortKey = event.detail.sortKey;
this.sortDesc = event.detail.sortDesc;
}
onReplay(event) { onReplay(event) {
event.preventDefault(); event.preventDefault();
const data = { const data = {
+30 -111
View File
File diff suppressed because one or more lines are too long
+1756 -943
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -1791,10 +1791,10 @@ electron-publish@22.7.0:
lazy-val "^1.0.4" lazy-val "^1.0.4"
mime "^2.4.5" mime "^2.4.5"
electron@^9.0.5: electron@9.1.2:
version "9.0.5" version "9.1.2"
resolved "https://registry.yarnpkg.com/electron/-/electron-9.0.5.tgz#189ee117cc2a2777cccf40fae0766acec5faae57" resolved "https://registry.yarnpkg.com/electron/-/electron-9.1.2.tgz#bfa26d6b192ea13abb6f1461371fd731a8358988"
integrity sha512-bnL9H48LuQ250DML8xUscsKiuSu+xv5umXbpBXYJ0BfvYVmFfNbG3jCfhrsH7aP6UcQKVxOG1R/oQExd0EFneQ== integrity sha512-xEYadr3XqIqJ4ktBPo0lhzPdovv4jLCpiUUGc2M1frUhFhwqXokwhPaTUcE+zfu5+uf/ONDnQApwjzznBsRrgQ==
dependencies: dependencies:
"@electron/get" "^1.0.1" "@electron/get" "^1.0.1"
"@types/node" "^12.0.12" "@types/node" "^12.0.12"