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:
@@ -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
@@ -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;
|
||||
|
||||
+136
-38
File diff suppressed because one or more lines are too long
+189
-27
@@ -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"> 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 <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>
|
||||
<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} </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>Filename: ${coll.filename}</i></p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<span class="size">Size: ${prettyBytes(Number(coll.size || 0))}</span>
|
||||
<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>`}
|
||||
</div>
|
||||
</div>
|
||||
`) : html`
|
||||
</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();
|
||||
|
||||
Reference in New Issue
Block a user