make archive index a sortable table,

make brand heading responsive, hidden on small screens, allow for more space for nav tabs
support copying source url
add date added property for new archives
use css for anim
This commit is contained in:
Ilya Kreymer
2020-05-22 08:47:55 -07:00
parent 9fb4c24b9e
commit 114abe662b
5 changed files with 339 additions and 161 deletions
-82
View File
@@ -1,82 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="66.712418mm"
height="66.712418mm"
viewBox="0 0 66.712418 66.712418"
version="1.1"
id="wrlogo">
<defs
id="defs1341">
<linearGradient
y2="48.223869"
x2="97.913383"
y1="48.223869"
x1="2.9740067"
gradientTransform="matrix(0.70268446,0,0,0.70268446,72.622704,112.65806)"
gradientUnits="userSpaceOnUse"
id="linearGradient6615"
xlink:href="#linearGradient5691" />
<linearGradient
id="linearGradient5691">
<stop
style="stop-color:#4876ff;stop-opacity:1"
offset="0"
id="stop5687">
<animate attributeName="stop-color" values="#4876ff; #1b0921; #4876ff; #04cdff; #4876ff; " dur="9s" repeatCount="indefinite" />
</stop>
<stop
style="stop-color:#04cdff;stop-opacity:1"
offset="1"
id="stop5689">
<animate attributeName="stop-color" values="#04cdff; #4876ff; #1b0921; #4876ff; #04cdff; " dur="9s" repeatCount="indefinite" />
</stop>
</linearGradient>
</defs>
<metadata
id="metadata1344">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
transform="translate(-72.477124,-114.81046)">
<circle
transform="rotate(0.86915873)"
r="33.356209"
cy="146.54422"
cx="108.06871"
id="path6016-0-7-6-9-11-2-8-67-6-50-4-33"
style="fill:url(#linearGradient6615);fill-opacity:1;stroke:none;stroke-width:23.3824;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
<path
style="fill:#d2f9d6;fill-opacity:1;stroke-width:0.702683"
d="m 119.62552,156.54036 h 5.46953 c 3.81136,0 6.91161,-3.10024 6.91161,-6.9116 0,-3.81136 -3.10025,-6.9116 -6.91161,-6.9116 h -15.59889 c -3.81136,0 -6.9116,3.10024 -6.9116,6.9116 0,1.96682 0.82917,3.73828 2.15161,4.9982 -0.83057,0.55722 -1.82557,0.88678 -2.89857,0.88678 h -2.082056 c -1.042775,-1.72016 -1.654819,-3.73054 -1.654819,-5.88498 0,-6.2834 5.112035,-11.39543 11.395435,-11.39543 h 15.59889 c 6.28341,0 11.39474,5.11132 11.39474,11.39543 0,6.28411 -5.11222,11.50105 -11.39474,11.39543 l -5.45665,0.14787 v -2.17722 z"
id="path114-4-0-85-6-0-5-3-6-1-6" />
<path
style="fill:#64e986;fill-opacity:1;stroke-width:0.702683"
d="m 91.878825,142.66568 -4.893158,0.0515 c -3.811155,0.0401 -6.911605,3.10024 -6.911605,6.91231 0,3.81065 3.100238,6.91089 6.911605,6.91089 h 15.598893 c 3.81136,0 6.9116,-3.10024 6.9116,-6.91089 0,-1.96682 -0.82917,-3.73899 -2.15162,-4.9989 0.83057,-0.55723 1.82557,-0.88609 2.89857,-0.88609 h 2.08066 c 1.04348,1.71947 1.65552,3.72985 1.65552,5.88499 0,6.28269 -5.11132,11.39472 -11.39473,11.39472 H 86.985667 c -6.282704,0 -11.39544,-5.11132 -11.39544,-11.39472 0,-6.28411 5.112828,-11.49617 11.39544,-11.39614 l 4.893158,0.0779 v 2.17722 z"
id="path116-9-4-7-0-8-3-2-08-9-1" />
<path
style="fill:#64e986;fill-opacity:1;stroke:none;stroke-width:15;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path3919-5-9-8-6-6-56-0-37-5-8"
d="m 73.728819,47.245763 -10.861072,-6.335289 -10.86107,-6.33529 10.917057,-6.238319 10.917057,-6.238318 -0.05599,12.573609 z"
transform="matrix(-0.38260367,0,0,0.3986412,118.95441,127.09397)" />
<path
style="fill:#d2f9d6;fill-opacity:1;stroke:none;stroke-width:15;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path3919-3-3-7-5-9-7-2-6-8-7-7"
d="m 73.728819,47.245763 -10.861072,-6.335289 -10.86107,-6.33529 10.917057,-6.238319 10.917057,-6.238318 -0.05599,12.573609 z"
transform="matrix(0.4122212,0,0,-0.40812,91.057524,172.76152)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

+1 -1
View File
@@ -20,7 +20,7 @@ $link: $wr-dark-blue;
$light: $wr-light-green;
$dark: $wr-dark-green;
$navbar-breakpoint: 800px;
$navbar-breakpoint: 840px;
.main-scroll {
flex: 1;
+141 -43
View File
File diff suppressed because one or more lines are too long
+194 -32
View File
@@ -3,7 +3,7 @@ import { unsafeHTML } from 'lit-html/directives/unsafe-html';
import { unsafeSVG } from 'lit-html/directives/unsafe-svg';
import { styleMap } from 'lit-html/directives/style-map';
import { digestMessage, getTS, tsToDate, sourceToId, parseURLSchemeHostPath } from './pageutils';
import { getTS, tsToDate, sourceToId, parseURLSchemeHostPath } from './pageutils';
import { register } from 'register-service-worker';
import prettyBytes from 'pretty-bytes';
@@ -11,11 +11,12 @@ import marked from 'marked';
import allCssRaw from '../assets/main.scss';
import rwpLogo from '../assets/logo.svg';
import rwpAnimLogo from '../assets/logoa.svg';
import fasArrowLeft from '@fortawesome/fontawesome-free/svgs/solid/arrow-left.svg';
import fasArrowRight from '@fortawesome/fontawesome-free/svgs/solid/arrow-right.svg';
import fasCopy from '@fortawesome/fontawesome-free/svgs/regular/copy.svg';
import fasInfoIcon from '@fortawesome/fontawesome-free/svgs/solid/info-circle.svg';
import fasRefresh from '@fortawesome/fontawesome-free/svgs/solid/redo-alt.svg';
@@ -69,7 +70,9 @@ class AppMain extends LitElement
return wrapCss(css`
#wrlogo {
max-height: 2.5rem;
margin-right: 8px;
}
.wr-logo-item {
padding-right: 8px;
}
.has-allcaps {
font-variant-caps: small-caps;
@@ -107,6 +110,14 @@ class AppMain extends LitElement
.info-menu {
padding: 0 1.0em;
}
.logo-text {
padding-left: 0px;
}
.logo-text:hover {
background-color: unset;
}
}
@media screen and (max-width: 800px) {
@@ -123,12 +134,11 @@ class AppMain extends LitElement
${!this.embed ? html`
<nav class="navbar has-background-info" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item has-text-weight-bold is-size-5 has-allcaps" href="/">
<a class="navbar-item wr-logo-item" title="ReplayWeb.page" href="/">
<fa-icon id="wrlogo" size="2.5rem" .svg=${rwpLogo}></fa-icon>
<span class="has-text-primary">replay</span>
<span class="has-text-link">web.page</span>
</a>
<a role="button" @click="${this.onNavMenu}" class="navbar-burger burger ${this.navMenuShown ? 'is-active' : ''}" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
<a role="button" @click="${this.onNavMenu}"
class="navbar-burger burger ${this.navMenuShown ? 'is-active' : ''}" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
@@ -136,6 +146,10 @@ class AppMain extends LitElement
</div>
<div class="navbar-menu ${this.navMenuShown ? 'is-active' : ''}">
<div class="navbar-start">
<a class="navbar-item logo-text has-text-weight-bold is-size-5 has-allcaps" href="/">
<span class="has-text-primary">replay</span>
<span class="has-text-link">web.page</span>
</a>
${IS_APP ? html`
<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>
@@ -511,7 +525,7 @@ You can select a file to upload from the main page by clicking the \'Choose File
return html`
<section class="container">
<div class="has-text-centered is-flex">
<fa-icon class="logo" size="96px" .svg=${rwpAnimLogo}></fa-icon>
<wr-anim-logo class="logo" size="96px" .svg=${rwpLogo}/>
</div>
<div class="level">
<p class="level-item">Loading&nbsp;<b>${this.sourceUrl}</b>...</p>
@@ -561,6 +575,10 @@ class WrIndex extends LitElement
super();
this.colls = [];
this.sortedColls = [];
this.sortKey = "title";
this.sortDesc = false;
this.fileDisplayName = "";
this.file = null;
@@ -570,6 +588,11 @@ class WrIndex extends LitElement
static get properties() {
return {
colls: { type: Array },
sortedColls: { type: Array },
sortKey: { type: String },
sortDesc: { type: Boolean },
fileDisplayName: { type: String },
_deleting: { type: Object }
}
@@ -579,6 +602,31 @@ class WrIndex extends LitElement
this.loadColls();
}
updated(changedProperties) {
if (changedProperties.has("colls") ||
changedProperties.has("sortKey") ||
changedProperties.has("sortDesc")) {
this.sortColls();
}
}
sortColls() {
this.sortedColls = [];
this.colls.forEach((coll) => {
coll.title = coll.title || coll.filename;
this.sortedColls.push(coll);
});
this.sortedColls.sort((first, second) => {
if (first[this.sortKey] === second[this.sortKey]) {
return 0;
}
return (this.sortDesc == (first[this.sortKey] < second[this.sortKey])) ? 1 : -1;
});
}
async loadColls() {
const resp = await fetch("./wabac/api/index");
try {
@@ -593,9 +641,11 @@ class WrIndex extends LitElement
async onDeleteColl(event) {
event.preventDefault();
event.stopPropagation();
const index = Number(event.currentTarget.getAttribute("data-coll-index"));
const coll = this.colls[index];
const coll = this.sortedColls[index];
if (!coll || this._deleting[coll.sourceUrl]) {
return;
}
@@ -603,7 +653,7 @@ class WrIndex extends LitElement
this._deleting[coll.sourceUrl] = true;
this.requestUpdate();
const resp = await fetch(`/wabac/api/${coll.id}`, {method: 'DELETE'});
const resp = await fetch(`./wabac/api/${coll.id}`, {method: 'DELETE'});
if (resp.status === 200) {
const json = await resp.json();
this.colls = json.colls;
@@ -663,7 +713,7 @@ class WrIndex extends LitElement
div.field.has-addons {
flex: auto;
}
form {
form, .level {
flex-grow: 1;
}
button.is-loading {
@@ -685,6 +735,39 @@ class WrIndex extends LitElement
margin-left: -1px;
}
table tr {
cursor: pointer;
}
tbody tr:hover {
background-color: aliceblue;
}
fa-icon {
vertical-align: middle;
}
.asc:after {
content: "▼";
font-size: 0.75em;
}
.desc:after {
content: "▲";
font-size: 0.75em;
}
.copy {
color: black;
margin: 0px;
margin: 0;
line-height: 0.4em;
padding: 6px;
border-radius: 10px;
display: none;
}
.copy:active {
background-color: lightgray;
}
.col-source:hover > .copy, .source-text:hover + .copy, .copy:hover {
display: initial;
}
`);
}
@@ -720,7 +803,7 @@ class WrIndex extends LitElement
.value="${this.fileDisplayName}"
@input="${this.onInput}"
autocomplete="off"
placeholder="Choose a local file or enter a URL for a (WARC, HAR, WBN, or WACZ) archive">
placeholder="Enter a URL or click 'Choose File' to select a (WARC, HAR, WBN, or WACZ) archive source">
</p>
<div class="control">
<button type="submit" class="button is-primary">Load</button>
@@ -736,26 +819,54 @@ class WrIndex extends LitElement
<nav class="panel is-light">
<p class="panel-heading">Loaded Archives</p>
<div class="coll-list">
${this.colls.length ? this.colls.map((coll, i) => html`
${this.sortedColls.length ? html`
<div class="panel-block">
<div class="level" style="width: 100%">
<div class="level-left">
<div>
<span class="subtitle"><a href="?source=${coll.sourceUrl}">${coll.title || coll.filename}</a></span>
<p><i>Source: ${coll.sourceUrl}</i></p>
${coll.sourceUrl && coll.sourceUrl.startsWith("googledrive://") ? html`
<p><i>Filename: ${coll.filename}</i></p>` : ''}
</div>
</div>
<div class="level-right">
<span class="size">Size: ${prettyBytes(Number(coll.size || 0))}</span>
${!this._deleting[coll.sourceUrl] ? html`
<button data-coll-index="${i}" @click="${this.onDeleteColl}" class="delete"></button>
` : html`
<button class="button is-loading is-static"></button>`}
</div>
</div>
`) : html`
<table class="table is-fullwidth">
<thead>
<tr>
<th @click="${this.onSort}" data-key="title"
class="${this.sortKey === "title" ? (this.sortDesc ? "desc" : "asc") : ''} col-title">
Title</th>
<th @click="${this.onSort}" data-key="sourceUrl"
class="${this.sortKey === "sourceUrl" ? (this.sortDesc ? "desc" : "asc") : ''} col-source">
Source URL (or Filename)</th>
<th @click="${this.onSort}" data-key="ctime"
class="${this.sortKey === "ctime" ? (this.sortDesc ? "desc" : "asc") : ''} col-ctime">
Date Loaded</th>
<th @click="${this.onSort}" data-key="size"
class="${this.sortKey === "size" ? (this.sortDesc ? "desc" : "asc") : ''} col-size">
Total Size</th>
<th></th>
</tr>
</thead>
<tbody>
${this.sortedColls.map((coll, i) => html`
<tr @click=${(e) => window.location.href = "?source=" + coll.sourceUrl}>
<td class="col-title"><span class="subtitle has-text-weight-bold">
<a href="?source=${coll.sourceUrl}">${coll.title || coll.filename}</a>
</td>
<td class="col-source"><span class="source-text">${coll.sourceUrl}&nbsp;</span>
<a @click="${(e) => this.onCopy(e, coll.sourceUrl)}" class="copy"><fa-icon .svg="${fasCopy}"/></a>
${coll.sourceUrl && coll.sourceUrl.startsWith("googledrive://") ? html`
<p><i>(${coll.filename})</i></p>` : ''}
</td>
<td class="col-date">${coll.ctime ? new Date(coll.ctime).toLocaleString() : ""}</td>
<td class="col-size">${prettyBytes(Number(coll.size || 0))}</td>
<td class="col-delete">
${!this._deleting[coll.sourceUrl] ? html`
<button data-coll-index="${i}" @click="${this.onDeleteColl}" class="delete"></button>
` : html`
<button class="button is-loading is-static"></button>`}
</td>
</tr>
`)}
</tbody>
</table>
</div>` : html`
<div class="panel-block extra-padding">
<i>No Archives so far! Archives loaded in the section above will appear here.</i>
</div>
@@ -765,6 +876,23 @@ class WrIndex extends LitElement
</section>
`;
}
onCopy(event, sourceUrl) {
event.preventDefault();
event.stopPropagation();
navigator.clipboard.writeText(sourceUrl);
return false;
}
onSort(event) {
const key = event.currentTarget.getAttribute("data-key");
if (key === this.sortKey) {
this.sortDesc = !this.sortDesc;
} else {
this.sortDesc = false;
this.sortKey = key;
}
}
}
// ===========================================================================
@@ -954,7 +1082,7 @@ class WrColl extends LitElement
}
}
@media screen and (min-width: ${!IS_APP ? css`680px` : css`850px`}) {
@media screen and (min-width: 320px) {
.main.tabs {
position: absolute;
top: 0px;
@@ -2136,6 +2264,39 @@ class WrFaIcon extends LitElement
}
// ===========================================================================
class WrAnimLogo extends WrFaIcon
{
static get styles() {
return css`
#wrlogo #stop5687 {
animation: animLeft 7s linear infinite;
}
#wrlogo #stop5689 {
animation: animRight 7s linear infinite;
}
@keyframes animLeft {
0% {stop-color: #4876ff}
25% {stop-color: #1b0921}
50% {stop-color: #4876ff}
75% {stop-color: #04cdff}
100% {stop-color: #4876ff}
}
@keyframes animRight {
0% {stop-color: #04cdff}
25% {stop-color: #4876ff}
50% {stop-color: #1b0921}
75% {stop-color: #4876ff}
100% {stop-color: #04cdff}
}
`;
}
}
// ===========================================================================
// ===========================================================================
@@ -2171,6 +2332,7 @@ async function main() {
customElements.define("wr-replay-page", WrReplayPage);
customElements.define("wr-gdrive", WrGdrive);
customElements.define("fa-icon", WrFaIcon);
customElements.define("wr-anim-logo", WrAnimLogo);
}
main();
+3 -3
View File
File diff suppressed because one or more lines are too long