sidebar support + pageview sidebar work!

- pages view now appears as sidebar in table/desktop sizes when clicking show pages button
- using <split-me> webcomponent to support adjustable splitter for sidebar
- pages view includes list filter if lists exist
- current page is highlighted when selected in sidebar
- date appears below url for each when in sidebar/mobile view mode
This commit is contained in:
Ilya Kreymer
2020-08-31 18:47:24 -07:00
parent 910e4795a5
commit 804529aeea
7 changed files with 517 additions and 178 deletions
+3 -2
View File
@@ -10,7 +10,7 @@
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^5.13.0", "@fortawesome/fontawesome-free": "^5.13.0",
"@webrecorder/wabac": "github:webrecorder/wabac.js", "@webrecorder/wabac": "github:webrecorder/wabac.js",
"bulma": "^0.8.2", "bulma": "^0.9.0",
"fetch-ndjson": "^1.1.0", "fetch-ndjson": "^1.1.0",
"flexsearch": "^0.6.32", "flexsearch": "^0.6.32",
"keyword-mark-element": "^0.1.2", "keyword-mark-element": "^0.1.2",
@@ -18,7 +18,8 @@
"marked": "^1.0.0", "marked": "^1.0.0",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"pretty-bytes": "^5.3.0", "pretty-bytes": "^5.3.0",
"register-service-worker": "^1.7.1" "register-service-worker": "^1.7.1",
"split-me": "^1.2.2"
}, },
"devDependencies": { "devDependencies": {
"css-loader": "^3.5.3", "css-loader": "^3.5.3",
+128 -31
View File
@@ -1,4 +1,4 @@
import { LitElement, html, css } from 'lit-element'; import { LitElement, html, css, query } from 'lit-element';
import { wrapCss, rwpLogo, IS_APP } from './misc'; import { wrapCss, rwpLogo, IS_APP } from './misc';
import { sourceToId, tsToDate } from './pageutils'; import { sourceToId, tsToDate } from './pageutils';
@@ -17,6 +17,8 @@ import fasLeft from '@fortawesome/fontawesome-free/svgs/solid/arrow-left.svg';
import fasRight from '@fortawesome/fontawesome-free/svgs/solid/arrow-right.svg'; import fasRight from '@fortawesome/fontawesome-free/svgs/solid/arrow-right.svg';
import fasMenuV from '@fortawesome/fontawesome-free/svgs/solid/ellipsis-v.svg'; import fasMenuV from '@fortawesome/fontawesome-free/svgs/solid/ellipsis-v.svg';
import { defineCustomElements as defineSplitMe } from 'split-me/loader';
const RWP_SCHEME = "search://"; const RWP_SCHEME = "search://";
@@ -55,6 +57,8 @@ class Coll extends LitElement
this.hasStory = false; this.hasStory = false;
this.editable = false; this.editable = false;
this.showSidebar = localStorage.getItem(`pages:showSidebar`) === "1";
} }
static get properties() { static get properties() {
@@ -64,6 +68,8 @@ class Coll extends LitElement
sourceUrl: { type: String }, sourceUrl: { type: String },
loadInfo: { type: Object, attribute: false }, loadInfo: { type: Object, attribute: false },
showSidebar: { type: Boolean },
collInfo: { type: Object, attribute: false }, collInfo: { type: Object, attribute: false },
coll: { type: String }, coll: { type: String },
@@ -91,6 +97,8 @@ class Coll extends LitElement
this.addEventListener("fullscreenchange", (event) => { this.addEventListener("fullscreenchange", (event) => {
this.isFullscreen = !!document.fullscreenElement; this.isFullscreen = !!document.fullscreenElement;
}); });
defineSplitMe(window);
} }
updated(changedProperties) { updated(changedProperties) {
@@ -202,6 +210,10 @@ class Coll extends LitElement
if (!this.hasStory && this.tabData.view === "story") { if (!this.hasStory && this.tabData.view === "story") {
this.tabData.view = "pages"; this.tabData.view = "pages";
} }
if (this.tabData.view === "replay" && this.tabData.query) {
this.showSidebar = true;
}
} }
onTabClick(event) { onTabClick(event) {
@@ -214,12 +226,26 @@ class Coll extends LitElement
onCollTabNav(event) { onCollTabNav(event) {
if (event.target.id === this.tabData.view) { if (event.target.id === this.tabData.view) {
this.updateTabData(event.detail.data, event.detail.replaceLoc); this.updateTabData(event.detail.data, event.detail.replaceLoc, false);
} else if (this.showSidebar && event.target.id === "pages" && this.tabData.view === "replay") {
if (event.detail.data.showSidebar === false) {
this.showSidebar = false;
return;
}
this.updateTabData(event.detail.data, event.detail.replaceLoc, true);
} }
} }
updateTabData(data, replaceLoc = false) { updateTabData(data, replaceLoc = false, merge = false) {
this.tabData = {view: this.tabData.view, ...data}; this.tabData = {...this.tabData, ...data};
// {
// view: this.tabData.view,
// query: this.tabData.query,
// currList: this.tabData.currList,
// ...data,
// };
if (this.tabData.url) { if (this.tabData.url) {
this.url = this.tabData.url || ""; this.url = this.tabData.url || "";
} }
@@ -232,6 +258,12 @@ class Coll extends LitElement
static get styles() { static get styles() {
return wrapCss(css` return wrapCss(css`
:host {
display: flex;
flex-direction: column;
height: 100%;
}
.icon { .icon {
vertical-align: text-top; vertical-align: text-top;
} }
@@ -282,7 +314,9 @@ class Coll extends LitElement
height: 100%; height: 100%;
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: row;
min-height: 0px;
flex: auto;
} }
${Coll.replayBarStyles} ${Coll.replayBarStyles}
@@ -356,6 +390,25 @@ class Coll extends LitElement
form { form {
width: 100%; width: 100%;
} }
.sidebar {
transition: transform 0.5s;
opacity: 1.0;
}
split-me {
--phantom-divider-thickness: 4px;
--divider-thickness: 0.1rem;
--divider-color: rgb(151, 152, 154);
--divider-shadow: 0;
width: 100%;
height: 100%;
display: block;
}
wr-page-view, wr-coll-replay {
width: 100%;
}
`; `;
} }
@@ -368,9 +421,9 @@ class Coll extends LitElement
.coll="${this.coll}" .sourceUrl="${this.sourceUrl}" @coll-loaded=${this.onCollLoaded}></wr-loader>`; .coll="${this.coll}" .sourceUrl="${this.sourceUrl}" @coll-loaded=${this.onCollLoaded}></wr-loader>`;
} else if (this.collInfo) { } else if (this.collInfo) {
return html` return html`
${this.renderLocationBar()}
${this.renderTabHeader()}
<nav id="contents" class="is-light"> <nav id="contents" class="is-light">
${this.renderLocationBar()}
${this.renderTabHeader()}
${this.renderCollTabs()} ${this.renderCollTabs()}
</nav>`; </nav>`;
} else { } else {
@@ -445,7 +498,7 @@ class Coll extends LitElement
` : ``} ` : ``}
</span> </span>
</button> </button>
<button class="button is-borderless is-hidden-touch ${!isReplay ? 'grey-disabled' : ''}" @click="${this.onGoPages}"> <button class="button is-borderless is-hidden-touch ${!isReplay ? 'grey-disabled' : ''}" @click="${this.onShowPages}">
<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>
@@ -491,7 +544,7 @@ class Coll extends LitElement
</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}"> <a class="dropdown-item is-hidden-desktop ${!isReplay ? 'grey-disabled' : ''}" @click="${this.onShowPages}">
<span class="icon is-small"> <span class="icon is-small">
<fa-icon size="1.0em" class="" .svg="${farListAlt}"></fa-icon> <fa-icon size="1.0em" class="" .svg="${farListAlt}"></fa-icon>
</span> </span>
@@ -523,12 +576,30 @@ class Coll extends LitElement
</div>`; </div>`;
} }
dragStart(event) {
const replay = this.renderRoot.querySelector("wr-coll-replay");
if (replay) {
replay.setDisablePointer(true);
}
event.path[0].addEventListener("mouseup", (event) => this.dragEnd(), {once: true});
}
dragEnd() {
const replay = this.renderRoot.querySelector("wr-coll-replay");
if (replay) {
replay.setDisablePointer(false);
}
}
renderCollTabs() { renderCollTabs() {
const isStory = this.tabData.view === 'story'; const isStory = this.tabData.view === 'story';
const isPages = this.tabData.view === 'pages'; const isPages = this.tabData.view === 'pages';
const isResources = this.tabData.view === 'resources'; const isResources = this.tabData.view === 'resources';
const isReplay = this.tabData.view === 'replay'; const isReplay = this.tabData.view === 'replay';
const isSidebar = isReplay && this.showSidebar;
return html` return html`
${this.hasStory ? html` ${this.hasStory ? html`
<wr-coll-story .collInfo="${this.collInfo}" <wr-coll-story .collInfo="${this.collInfo}"
@@ -538,15 +609,6 @@ class Coll extends LitElement
class="${isStory ? '' : 'is-hidden'}"> class="${isStory ? '' : 'is-hidden'}">
</wr-coll-story>` : ''} </wr-coll-story>` : ''}
<wr-page-view .collInfo="${this.collInfo}"
.active="${isPages}"
.editable="${this.editable}"
currList="${this.tabData.currList || 0}"
query="${this.tabData.query || ""}"
@coll-tab-nav="${this.onCollTabNav}" id="pages"
class="${isPages ? '' : 'is-hidden'}">
</wr-page-view>
<wr-coll-resources .collInfo="${this.collInfo}" <wr-coll-resources .collInfo="${this.collInfo}"
.active="${isResources}" .active="${isResources}"
query="${this.tabData.query || ""}" query="${this.tabData.query || ""}"
@@ -556,16 +618,42 @@ class Coll extends LitElement
class="is-paddingless ${isResources ? '' : 'is-hidden'}"> class="is-paddingless ${isResources ? '' : 'is-hidden'}">
</wr-coll-resources> </wr-coll-resources>
${isReplay ? html` <split-me n="${isSidebar ? 2 : 1}"
<wr-coll-replay .collInfo="${this.collInfo}" d="horizontal"
sourceUrl="${this.sourceUrl}" sizes="0.30, 0.70"
url="${this.tabData.url || ""}" .minSizes=${[0.10, 0.50]}
ts="${this.tabData.ts || ""}" @dragstart="${this.dragStart}"
@coll-tab-nav="${this.onCollTabNav}" id="replay" class="${isReplay || isPages ? '' : 'is-hidden'}"
@replay-loading="${(e) => this.isLoading = e.detail.loading}" >
class="${isReplay ? '' : 'is-hidden'}"> <wr-page-view
</wr-coll-replay> slot="0"
` : ``} .collInfo="${this.collInfo}"
.active="${isPages}"
.editable="${this.editable}"
.isSidebar="${isSidebar}"
currList="${this.tabData.currList || 0}"
query="${this.tabData.query || ""}"
.url="${this.tabData.url || ""}"
.ts="${this.tabData.ts || ""}"
@coll-tab-nav="${this.onCollTabNav}" id="pages"
class="${isSidebar ? 'sidebar' : (isPages ? '' : 'is-hidden')}">
</wr-page-view>
${isReplay ? html`
<wr-coll-replay
slot="${isSidebar ? 1 : 0}"
.collInfo="${this.collInfo}"
sourceUrl="${this.sourceUrl}"
url="${this.tabData.url || ""}"
ts="${this.tabData.ts || ""}"
@coll-tab-nav="${this.onCollTabNav}" id="replay"
@replay-loading="${(e) => this.isLoading = e.detail.loading}"
class="${isReplay ? '' : 'is-hidden'}">
</wr-coll-replay>
` : ``}
</split-me>
`; `;
} }
@@ -605,11 +693,20 @@ class Coll extends LitElement
window.history.forward(); window.history.forward();
} }
onGoPages(event) { onShowPages(event) {
event.preventDefault(); event.preventDefault();
if (this.tabData && this.tabData.view === "replay") { // show sidebar if table or greater
this.updateTabData({view: "pages"}); if (this.showSidebar || (document.documentElement.clientWidth >= 769)) {
this.showSidebar = !this.showSidebar;
//this.updateTabData({showSidebar: !this.showSidebar});
} else {
// otherwise, just go to full pages view
//this.showSidebar = false;
if (this.tabData && this.tabData.view === "replay") {
this.updateTabData({view: "pages"});
}
} }
localStorage.setItem(`pages:showSidebar`, this.showSidebar ? "1" : "0");
} }
onReAuthed(event) { onReAuthed(event) {
+359 -139
View File
@@ -14,6 +14,9 @@ import { getTS } from './pageutils';
import fasSearch from '@fortawesome/fontawesome-free/svgs/solid/search.svg'; import fasSearch from '@fortawesome/fontawesome-free/svgs/solid/search.svg';
import fasAngleDown from '@fortawesome/fontawesome-free/svgs/solid/angle-down.svg'; import fasAngleDown from '@fortawesome/fontawesome-free/svgs/solid/angle-down.svg';
import fasLeft from '@fortawesome/fontawesome-free/svgs/solid/angle-left.svg';
import fasRight from '@fortawesome/fontawesome-free/svgs/solid/angle-right.svg';
// =========================================================================== // ===========================================================================
class Pages extends LitElement class Pages extends LitElement
@@ -40,6 +43,10 @@ class Pages extends LitElement
this.sortKey = "ts"; this.sortKey = "ts";
this.sortDesc = true; this.sortDesc = true;
this.isSidebar = false;
this.url = "";
this.ts = "";
} }
static get sortKeys() { static get sortKeys() {
@@ -79,7 +86,11 @@ class Pages extends LitElement
menuActive: {type: Boolean }, menuActive: {type: Boolean },
sortKey: { type: String }, sortKey: { type: String },
sortDesc: { type: Boolean } sortDesc: { type: Boolean },
isSidebar: { type: Boolean },
url: { type: String },
ts: { type: String }
} }
} }
@@ -91,7 +102,7 @@ class Pages extends LitElement
} }
} }
updated(changedProperties) { async updated(changedProperties) {
if (changedProperties.has("collInfo")) { if (changedProperties.has("collInfo")) {
this.updateTextSearch(); this.updateTextSearch();
} else if (changedProperties.has("query")) { } else if (changedProperties.has("query")) {
@@ -109,6 +120,16 @@ class Pages extends LitElement
this.filter(); this.filter();
} }
} }
if (changedProperties.has("sortedPages") && this.isSidebar) {
//if (await this.updateComplete) {
const selected = this.renderRoot.querySelector(".current");
if (selected) {
const opts = {behavior: "smooth", block: "nearest", inline: "nearest"};
setTimeout(() => selected.scrollIntoView(opts), 100);
}
//}
}
} }
onChangeQuery(event) { onChangeQuery(event) {
@@ -150,16 +171,18 @@ class Pages extends LitElement
const resp = await fetch(`${this.collInfo.apiPrefix}/curated/${this.currList}`); const resp = await fetch(`${this.collInfo.apiPrefix}/curated/${this.currList}`);
const json = await resp.json(); const json = await resp.json();
this.sortedPages = []; const curated = [];
for (const c of json.curated) { for (const c of json.curated) {
for (const p of this.filteredPages) { for (const p of this.filteredPages) {
if (p.id === c.page) { if (p.id === c.page) {
this.sortedPages.push(p); curated.push(p);
break; break;
} }
} }
} }
this.filteredPages = curated;
} }
sendChangeEvent(data) { sendChangeEvent(data) {
@@ -239,14 +262,16 @@ class Pages extends LitElement
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
min-width: 0px;
flex-direction: column; flex-direction: column;
box-sizing: border-box !important;
} }
.main.columns { .main.columns {
width: 100%; width: 100%;
justify-self: stretch; justify-self: stretch;
margin: 1.0em 0 0 0;
min-height: 0px; min-height: 0px;
margin: 0px;
} }
.header.columns { .header.columns {
@@ -270,7 +295,7 @@ class Pages extends LitElement
margin-left: 0.75em; margin-left: 0.75em;
} }
.sidebar { .index-bar {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-right: 3px solid rgb(237, 237, 237); border-right: 3px solid rgb(237, 237, 237);
@@ -278,21 +303,21 @@ class Pages extends LitElement
padding-right: 0px; padding-right: 0px;
} }
.sidebar-title { .index-bar-title {
font-size: 1.25rem; font-size: 1.25rem;
text-transform: uppercase; text-transform: uppercase;
margin-bottom: 1.0rem; margin-bottom: 1.0rem;
word-break: break-word; word-break: break-word;
} }
.sidebar-status { .index-bar-status {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
padding-right: 0.75em; padding-right: 0.75em;
} }
.sidebar-menu { .index-bar-menu {
margin-top: 1.0rem; margin-top: 1.0rem;
} }
@@ -303,6 +328,7 @@ class Pages extends LitElement
.num-results { .num-results {
font-style: italic; font-style: italic;
font-weight: normal; font-weight: normal;
line-height: 2.5;
} }
.asc:after { .asc:after {
@@ -314,60 +340,51 @@ class Pages extends LitElement
font-size: 0.75em; font-size: 0.75em;
} }
@media screen and (min-width: 768px) { @media screen and (min-width: 769px) {
.main.columns { .full .main.columns {
max-height: 100%; max-height: 100%;
height: 100%; height: 100%;
} }
.sidebar-menu { .full .index-bar-menu {
max-height: 100%; max-height: 100%;
overflow-y: auto; overflow-y: auto;
} }
} }
@media screen and (max-width: 767px) { @media screen and (max-width: 768px) {
.main.columns { ${Pages.sidebarStyles()}
position: relative; }
max-height: 100%;
height: 100%;
}
.sidebar-menu {
max-height: 75px;
overflow-y: auto;
margin-top: 0.75em;
}
.column.main-content {
position: relative;
overflow-y: auto;
border-top: 1px solid black;
width: 100%;
min-height: 0px;
height: 100%;
padding: 0px;
margin: 0px;
}
.mobile-header { ${Pages.sidebarStyles(css`.sidebar `)}
margin: 0.5rem;
margin-left: 1.0rem;
display: flex;
justify-content: space-between;
flex-direction: row;
min-height: 24px;
width: 100%;
}
.menu { .full, .sidebar {
font-size: 0.80rem; min-height: 0px;
} margin: 0;
display: flex;
flex-direction: column;
height: 100%;
}
.mobile-lists {
display: block !important;
}
.sidebar .columns.is-hidden-mobile, .sidebar .is-hidden-mobile {
display: none !important;
}
.sidebar .mobile-header {
display: flex !important;
}
.sidebar .columns {
display: flex !important;
} }
.scroller { .scroller {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: auto; flex: auto;
@@ -375,13 +392,14 @@ class Pages extends LitElement
padding-bottom: 1.0em; padding-bottom: 1.0em;
min-height: 0px; min-height: 0px;
} }
.selected {
background-color: ghostwhite; .current {
/*background-color: rgb(207, 243, 255);*/
} }
.page-header { .page-header {
display: flex; display: flex;
flex-direction: row; flex-flow: row wrap;
width: 100%; width: 100%;
min-height: fit-content; min-height: fit-content;
@@ -394,25 +412,95 @@ class Pages extends LitElement
} }
.search-bar { .search-bar {
width: 100%; width: auto;
display: flex; display: flex;
flex-direction: row; flex-direction: column;
} }
.flex-auto { .flex-auto {
flex: auto; flex: auto;
} }
.sidebar-nav {
display: flex;
justify-content: space-between;
vertical-align: middle;
}
.sidebar-nav span {
vertical-align: baseline;
}
.sidebar-nav fa-icon {
vertical-align: middle;
}
`); `);
} }
static sidebarStyles(prefix = css``) {
return css`
${prefix} .main.columns {
position: relative;
max-height: 100%;
height: 100%;
}
${prefix} .index-bar-menu {
max-height: 75px;
overflow-y: auto;
margin-top: 0.75em;
}
${prefix} .column.main-content {
position: relative;
overflow-y: auto;
width: 100%;
min-height: 0px;
height: 100%;
padding: 0px;
margin: 0px;
}
${prefix} .mobile-header {
margin: 0.5rem;
margin-left: 1.0rem;
align-items: center;
display: flex;
justify-content: space-between;
flex-flow: row wrap;
min-height: 24px;
width: 100%;
}
${prefix} .menu {
font-size: 0.80rem;
}`;
}
onSelectList(event) { onSelectList(event) {
event.preventDefault(); event.preventDefault();
this.currList = Number(event.currentTarget.getAttribute("data-list")); this.currList = Number(event.currentTarget.getAttribute("data-list"));
} }
onSelectListDrop(event) {
event.preventDefault();
this.currList = Number(event.currentTarget.value);
}
render() { render() {
const currList = this.currList; const currList = this.currList;
return html` return html`
${this.isSidebar ? html`
<div class="sidebar-nav">
<a @click="${this.onHideSidebar}" class="is-marginless is-size-6 is-paddingless">
<fa-icon .svg="${fasLeft}"></fa-icon><span>Hide</span>
</a>
<a @click="${this.onFullPageView}" class="is-marginless is-size-6 is-paddingless">
<span>Full Page View</span><fa-icon .svg="${fasRight}"></fa-icon>
</a>
</div>
` : ``}
<div class="search-bar notification is-marginless"> <div class="search-bar notification is-marginless">
<div class="field flex-auto"> <div class="field flex-auto">
<div class="control has-icons-left ${this.loading ? 'is-loading' : ''}"> <div class="control has-icons-left ${this.loading ? 'is-loading' : ''}">
@@ -421,39 +509,55 @@ class Pages extends LitElement
<span class="icon is-left"><fa-icon .svg="${fasSearch}"/></span> <span class="icon is-left"><fa-icon .svg="${fasSearch}"/></span>
</div> </div>
</div> </div>
${this.isSidebar && this.collInfo.lists.length ? html`
<div class="is-hidden-tablet mobile-lists">
<span class="is-size-7">Filter By List:</span>
<div class="select is-small">
<select id="sort-select" @change=${this.onSelectListDrop}>
<option value="0" ?selected="${this.currList === 0}">All Pages</option>
${this.collInfo.lists.map(list => html`
<option value="${list.id}" ?selected="${this.currList === list.id}">${list.title}</option>
`)}
</select>
</div>
</div>` : ``}
</div> </div>
<div class="main columns"> <div class="${this.isSidebar ? "sidebar" : "full"}">
<div class="column sidebar is-one-fifth is-hidden-mobile"> <div class="main columns">
<div class="sidebar-title">${this.collInfo.title}</div> <div class="column index-bar is-one-fifth is-hidden-mobile">
<div class="index-bar-title">${this.collInfo.title}</div>
<span class="num-results">${this.formatResults()}</span> <span class="num-results">${this.formatResults()}</span>
${this.editable ? html` ${this.editable ? html`
<div class="sidebar-actions"> <div class="index-bar-actions">
${this.renderDownloadMenu()} ${this.renderDownloadMenu()}
</div>` : ``} </div>` : ``}
${this.collInfo.lists.length ? html` ${this.collInfo.lists.length ? html`
<p id="filter-label" class="menu-label">Filter By List:</p> <p id="filter-label" class="menu-label">Filter By List:</p>
<aside class="sidebar-menu menu"> <aside class="index-bar-menu menu">
<ul class="menu-list"> <ul class="menu-list">
<li>
<a href="#list-0" data-list="0" class="${currList === 0 ? 'is-active' : ''}"
@click=${this.onSelectList}><i>All Pages</i></a>
</li>
${this.collInfo.lists.map(list => html`
<li> <li>
<a @click=${this.onSelectList} href="#list-${list.id}" <a href="#list-0" data-list="0" class="${currList === 0 ? 'is-active' : ''}"
data-list="${list.id}" @click=${this.onSelectList}><i>All Pages</i></a>
class="${currList === list.id ? 'is-active' : ''}">${list.title}</a> </li>
</li>`)} ${this.collInfo.lists.map(list => html`
</ul> <li>
</aside> <a @click=${this.onSelectList} href="#list-${list.id}"
` : ``} data-list="${list.id}"
</div> class="${currList === list.id ? 'is-active' : ''}">${list.title}</a>
<div class="column main-content"> </li>`)}
${this.renderPages()} </ul>
</aside>
` : ``}
</div>
<div class="column main-content">
${this.renderPages()}
</div>
</div> </div>
</div>`; </div>`;
} }
@@ -506,8 +610,13 @@ class Pages extends LitElement
<a @click="${this.onSort}" data-key="title" class="column is-6 pagetitle ${this.sortKey === "title" ? (this.sortDesc ? "desc" : "asc") : ''}">Page Title</a> <a @click="${this.onSort}" data-key="title" class="column is-6 pagetitle ${this.sortKey === "title" ? (this.sortDesc ? "desc" : "asc") : ''}">Page Title</a>
</div> </div>
<div class="is-hidden-tablet mobile-header"> <div class="is-hidden-tablet mobile-header">
<span class="num-results">${this.formatResults()}</span>
<div class="num-results">${this.formatResults()}</div>
<wr-sorter id="pages" <wr-sorter id="pages"
.sortKey="${this.sortKey}" .sortKey="${this.sortKey}"
.sortDesc="${this.sortDesc}" .sortDesc="${this.sortDesc}"
@@ -520,6 +629,26 @@ class Pages extends LitElement
`; `;
} }
isCurrPage(page) {
if (this.isSidebar) {
if (page.url === this.url) {
let ts = page.timestamp;
if (!ts && page.date) {
ts = getTS(page.date);
} else if (typeof(page.ts) === "string") {
ts = getTS(page.ts);
}
return ts === this.ts;
}
}
if (this.editable) {
return this.selectedPages.has(p.id);
}
return false;
}
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`
@@ -528,11 +657,24 @@ class Pages extends LitElement
</div> </div>
<div class="scroller" @scroll="${this.onScroll}"> <div class="scroller" @scroll="${this.onScroll}">
${this.sortedPages.length ? html` ${this.sortedPages.length ? html`
${this.sortedPages.map((p, i) => html` ${this.sortedPages.map((p, i) => {
<div class="content ${this.selectedPages.has(p.id) ? 'selected' : ''}"> const isCurrPage = this.isCurrPage(p);
<wr-page-entry .index="${this.query ? (this.sortDesc ? this.sortedPages.length - i : i + 1) : 0}" .editable="${this.editable}" .selected="${this.selectedPages.has(p.id)}" @sel-page="${this.onSelectToggle}" @delete-page="${this.onDeletePage}" replayPrefix="${this.collInfo.replayPrefix}" query="${this.query}" .page="${p}">
return html`
<div class="content ${isCurrPage ? 'current' : ''}">
<wr-page-entry
.index="${this.query || this.isSidebar ? i + 1 : 0}"
.editable="${this.editable}"
.selected="${this.selectedPages.has(p.id)}"
.isSidebar="${this.isSidebar}"
.isCurrent="${isCurrPage}"
@sel-page="${this.onSelectToggle}"
@delete-page="${this.onDeletePage}"
replayPrefix="${this.collInfo.replayPrefix}"
query="${this.query}"
.page="${p}">
</wr-page-entry> </wr-page-entry>
</div>`)}` : html`<p class="mobile-header">${this.getNoResultsMessage()}</p>`} </div>` })}` : html`<p class="mobile-header">${this.getNoResultsMessage()}</p>`}
</div> </div>
`; `;
} }
@@ -591,6 +733,24 @@ class Pages extends LitElement
this.requestUpdate(); this.requestUpdate();
} }
onFullPageView(event) {
event.preventDefault();
const data = {
view: "pages",
url: "",
ts: "",
};
this.sendChangeEvent(data);
}
onHideSidebar(event) {
event.preventDefault();
const data = {
showSidebar: false
};
this.sendChangeEvent(data);
}
async onDownload(event, format, selected) { async onDownload(event, format, selected) {
event.preventDefault(); event.preventDefault();
@@ -677,6 +837,11 @@ class PageEntry extends LitElement
this.editable = false; this.editable = false;
this.iconValid = false; this.iconValid = false;
this.index = 0; this.index = 0;
this.isSidebar = false;
this.isCurrent = false;
this.timestamp = "";
this.date = null;
} }
static get properties() { static get properties() {
@@ -690,6 +855,10 @@ class PageEntry extends LitElement
editable: { type: Boolean }, editable: { type: Boolean },
iconValid: { type: Boolean }, iconValid: { type: Boolean },
index: { type: Number }, index: { type: Number },
isSidebar: { type: Boolean },
isCurrent: { type: Boolean },
timestamp: { type: String },
date: { type: Object }
} }
} }
@@ -715,6 +884,19 @@ class PageEntry extends LitElement
width: 100%; width: 100%;
} }
.full {
flex: auto;
min-height: 0px;
}
.sidebar .column {
width: unset !important;
}
.sidebar {
width: 100%;
}
.favicon { .favicon {
width: 24px !important; width: 24px !important;
height: 24px !important; height: 24px !important;
@@ -739,33 +921,66 @@ class PageEntry extends LitElement
background-color: rgb(241, 70, 104); background-color: rgb(241, 70, 104);
} }
@media screen and (max-width: 767px) { @media screen and (max-width: 768px) {
.col-date { ${PageEntry.sidebarStyles()}
margin-left: calc(24px + 1rem); }
}
.col-date div { ${PageEntry.sidebarStyles(css`.sidebar `)}
display: inline;
} .current a {
.col-index { background-color: rgb(207, 243, 255);
position: absolute; font-style: italic;
top: 0px; display: block;
left: 0px; }
margin-top: -0.75em;
} .current .curr-page {
.columns { font-style: italic;
display: flex; font-size: 9px;
flex-direction: column-reverse; color: black;
} }
.is-inline-date {
display: none;
} }
`); `);
} }
static sidebarStyles(prefix = css``) {
return css`
${prefix} .col-date {
margin-left: calc(24px + 1rem);
display: none;
}
${prefix} .col-date div {
display: inline;
}
${prefix} .col-index {
position: absolute;
top: 0px;
left: 0px;
margin-top: -0.75em;
}
${prefix} .columns {
display: flex;
flex-direction: column-reverse;
}
${prefix} .is-inline-date {
display: initial !important;
font-style: italic;
}
`;
}
updated(changedProperties) { updated(changedProperties) {
if (changedProperties.has("page") || changedProperties.has("query")) { if (changedProperties.has("page") || changedProperties.has("query")) {
this.updateSnippet(); this.updateSnippet();
this.iconValid = !!this.page.favIconUrl; this.iconValid = !!this.page.favIconUrl;
//this.updateFavIcon(); //this.updateFavIcon();
this.deleting = false; this.deleting = false;
const res = this.getDateTS();
this.timestamp = res.timestamp;
this.date = res.date;
} }
} }
@@ -784,7 +999,7 @@ class PageEntry extends LitElement
render() { render() {
const p = this.page; const p = this.page;
const {date, timestamp} = this.getDateTS(); const date = this.date;
const hasSize = typeof(p.size) === "number"; const hasSize = typeof(p.size) === "number";
@@ -796,38 +1011,44 @@ class PageEntry extends LitElement
</label> </label>
</div>` : ``} </div>` : ``}
<div class="columns"> <div class="${this.isSidebar ? "sidebar" : "full"}" @click="${this.onReplay}">
${this.index ? html` <div class="columns">
<div class="column col-index is-1">${this.index}.</div> ${this.index ? html`
` : ``} <div class="column col-index is-1 is-size-7">${this.index}.</div>
<div class="column col-date is-2"> ` : ``}
<div>${date ? date.toLocaleDateString() : ""}</div> <div class="column col-date is-2">
<div>${date ? date.toLocaleTimeString() : ""}</div> <div>${date ? date.toLocaleDateString() : ""}</div>
</div> <div>${date ? date.toLocaleTimeString() : ""}</div>
<div class="column"> </div>
<article class="media"> <div class="column">
<figure class="media-left"> <article class="media">
<p class=""> <figure class="media-left">
${this.iconValid ? html` <p class="">
<img class="favicon" @error="${(e) => this.iconValid = false}" src="${this.replayPrefix}/${timestamp}id_/${p.favIconUrl}"/>` : html` ${this.iconValid ? html`
<span class="favicon"></span>`} <img class="favicon" @error="${(e) => this.iconValid = false}" src="${this.replayPrefix}/${this.timestamp}id_/${p.favIconUrl}"/>` : html`
</p> <span class="favicon"></span>`}
</figure>
<div class="media-content">
<a @click="${this.onReplay}" data-url="${p.url}" data-ts="${timestamp}" href="#">
<p class="is-size-6 has-text-weight-bold has-text-link text">
<keyword-mark keywords="${this.query}">${p.title || p.url}</keyword-mark>
</p> </p>
<p class="has-text-dark text"><keyword-mark keywords="${this.query}">${p.url}</keyword-mark></p> </figure>
</a> <div class="media-content ${this.isCurrent ? 'current' : ''}">
${this.textSnippet ? html` <a @click="${this.onReplay}" href="#">
<div class="text"><keyword-mark keywords="${this.query}">${this.textSnippet}</keyword-mark></div>` : html``} ${this.isCurrent ? html`<p class="curr-page is-pulled-right">Current Page</p>` : ``}
</div> <p class="is-size-6 has-text-weight-bold has-text-link text">
${hasSize ? html` <keyword-mark keywords="${this.query}">${p.title || p.url}</keyword-mark>
<div class="media-right" style="margin-right: 2em"> </p>
${prettyBytes(p.size)} <p class="has-text-dark text"><keyword-mark keywords="${this.query}">${p.url}</keyword-mark></p>
</div>` : ``} <p class="has-text-grey-dark text is-inline-date">
</article> ${date ? date.toLocaleString(): ""}
</p>
</a>
${this.textSnippet ? html`
<div class="text"><keyword-mark keywords="${this.query}">${this.textSnippet}</keyword-mark></div>` : html``}
</div>
${hasSize ? html`
<div class="media-right" style="margin-right: 2em">
${prettyBytes(p.size)}
</div>` : ``}
</article>
</div>
</div> </div>
</div> </div>
@@ -845,9 +1066,7 @@ class PageEntry extends LitElement
return; return;
} }
const {timestamp} = this.getDateTS(); const resp = await fetch(`${this.replayPrefix}/${this.timestamp}id_/${this.page.favIconUrl}`);
const resp = await fetch(`${this.replayPrefix}/${timestamp}id_/${this.page.favIconUrl}`);
if (resp.status != 200) { if (resp.status != 200) {
this.favIconData = null; this.favIconData = null;
@@ -911,9 +1130,10 @@ class PageEntry extends LitElement
onReplay(event) { onReplay(event) {
event.preventDefault(); event.preventDefault();
const data = { const data = {
url: event.currentTarget.getAttribute("data-url"), url: this.page.url,
ts: event.currentTarget.getAttribute("data-ts"), ts: this.timestamp,
view: "replay" view: "replay"
}; };
this.sendChangeEvent(data); this.sendChangeEvent(data);
+9 -1
View File
@@ -86,6 +86,14 @@ class Replay extends LitElement
} }
} }
setDisablePointer(disable) {
const iframe = this.renderRoot.querySelector("iframe");
if (iframe) {
iframe.style.pointerEvents = disable ? "none" : "all";
}
}
onReplayMessage(event) { onReplayMessage(event) {
const iframe = this.renderRoot.querySelector("iframe"); const iframe = this.renderRoot.querySelector("iframe");
@@ -154,7 +162,7 @@ class Replay extends LitElement
} }
iframe { iframe {
width: 100vw; width: 100%;
#height: calc(100vh - 150px); #height: calc(100vh - 150px);
height: 100%; height: 100%;
border: 0px; border: 0px;
+1
View File
@@ -113,6 +113,7 @@ class Sorter extends LitElement
return wrapCss(css` return wrapCss(css`
:host { :host {
min-width: 100px; min-width: 100px;
box-sizing: border-box !important;
} }
button.button.is-small { button.button.is-small {
border-radius: 4px; border-radius: 4px;
+1 -1
View File
@@ -94,6 +94,7 @@ class Story extends LitElement
.columns { .columns {
width: 100%; width: 100%;
height: 100%;
justify-self: stretch; justify-self: stretch;
margin: 1.0em 0 0 0; margin: 1.0em 0 0 0;
min-height: 0; min-height: 0;
@@ -146,7 +147,6 @@ class Story extends LitElement
overflow-y: auto; overflow-y: auto;
border-top: 1px solid black; border-top: 1px solid black;
width: 100%;
height: 100%; height: 100%;
max-height: calc(100% - 150px - 0.75em); max-height: calc(100% - 150px - 0.75em);
} }
+16 -4
View File
@@ -916,10 +916,10 @@ builtin-status-codes@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
bulma@^0.8.2: bulma@^0.9.0:
version "0.8.2" version "0.9.0"
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.8.2.tgz#5d928f16ed4a84549c2873f95c92c38c69c631a7" resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.9.0.tgz#948c5445a49e9d7546f0826cb3820d17178a814f"
integrity sha512-vMM/ijYSxX+Sm+nD7Lmc1UgWDy2JcL2nTKqwgEqXuOMU+IGALbXd5MLt/BcjBAPLIx36TtzhzBcSnOP974gcqA== integrity sha512-rV75CJkubNUroAt0qCRkjznZLoaXq/ctfMXsMvKSL84UetbSyx5REl96e8GoQ04G4Tkw0XF3STECffTOQrbzOQ==
bytes@3.0.0: bytes@3.0.0:
version "3.0.0" version "3.0.0"
@@ -3530,6 +3530,11 @@ locate-path@^5.0.0:
dependencies: dependencies:
p-locate "^4.1.0" p-locate "^4.1.0"
lodash.throttle@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@~4.17.12: lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@~4.17.12:
version "4.17.15" version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
@@ -5405,6 +5410,13 @@ spdy@^4.0.1:
select-hose "^2.0.0" select-hose "^2.0.0"
spdy-transport "^3.0.0" spdy-transport "^3.0.0"
split-me@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/split-me/-/split-me-1.2.2.tgz#64c1641f455d61662e7674cca4896f4ee4e72792"
integrity sha512-wZF3TUNVn/DGoLfMlg3wFbYmh+vhLDSZ6fcIVcVH6xuf/v9Es4aXcynYvEiyO71zniv4TqsUEhQkwPJ1xnz5FQ==
dependencies:
lodash.throttle "^4.1.1"
split-string@^3.0.1, split-string@^3.0.2: split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"