misc ui tweaks, mostly pages:
- add download menu - add selection hilite - tweak colors on chooser disable notarize for now
This commit is contained in:
@@ -48,7 +48,6 @@
|
|||||||
"all": "yarn run build && yarn run docs && yarn run pack"
|
"all": "yarn run build && yarn run docs && yarn run pack"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"afterSign": "build/notarize.js",
|
|
||||||
"productName": "ReplayWeb.page",
|
"productName": "ReplayWeb.page",
|
||||||
"asar": true,
|
"asar": true,
|
||||||
"appId": "net.webrecorder.replaywebpage",
|
"appId": "net.webrecorder.replaywebpage",
|
||||||
|
|||||||
+4
-1
@@ -64,6 +64,9 @@ class Chooser extends LitElement
|
|||||||
div.field.has-addons {
|
div.field.has-addons {
|
||||||
flex: auto;
|
flex: auto;
|
||||||
}
|
}
|
||||||
|
.panel-heading {
|
||||||
|
background-color: #cff3ff;
|
||||||
|
}
|
||||||
form {
|
form {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -94,7 +97,7 @@ class Chooser extends LitElement
|
|||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<section class="section less-padding">
|
<section class="section less-padding">
|
||||||
<nav class="panel is-warning">
|
<nav class="panel">
|
||||||
<p class="panel-heading">Load Web Archive</p>
|
<p class="panel-heading">Load Web Archive</p>
|
||||||
<div class="extra-padding panel-block file has-name">
|
<div class="extra-padding panel-block file has-name">
|
||||||
<form class="content is-flex" @submit="${this.onStartLoad}">
|
<form class="content is-flex" @submit="${this.onStartLoad}">
|
||||||
|
|||||||
+2
-1
@@ -56,6 +56,7 @@ class Coll extends LitElement
|
|||||||
replayTS: { type: String },
|
replayTS: { type: String },
|
||||||
|
|
||||||
embed: { type: String },
|
embed: { type: String },
|
||||||
|
editable: { type: Boolean }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +174,6 @@ class Coll extends LitElement
|
|||||||
this.tabData = {view: this.tabData.view, ...event.detail.data};
|
this.tabData = {view: this.tabData.view, ...event.detail.data};
|
||||||
this._replaceLoc = !this._locUpdateNeeded && event.detail.replaceLoc;
|
this._replaceLoc = !this._locUpdateNeeded && event.detail.replaceLoc;
|
||||||
this._locUpdateNeeded = true;
|
this._locUpdateNeeded = true;
|
||||||
console.log(this.tabData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +307,7 @@ class Coll extends LitElement
|
|||||||
|
|
||||||
<wr-page-view .collInfo="${this.collInfo}"
|
<wr-page-view .collInfo="${this.collInfo}"
|
||||||
.active="${isPages}"
|
.active="${isPages}"
|
||||||
|
.editable="${this.editable}"
|
||||||
currList="${this.tabData.currList || 0}"
|
currList="${this.tabData.currList || 0}"
|
||||||
query="${this.tabData.query || ""}"
|
query="${this.tabData.query || ""}"
|
||||||
@coll-tab-nav="${this.onCollTabNav}" id="pages"
|
@coll-tab-nav="${this.onCollTabNav}" id="pages"
|
||||||
|
|||||||
+19
-3
@@ -15,7 +15,7 @@ function wrapCss(custom) {
|
|||||||
return [allCss, custom];
|
return [allCss, custom];
|
||||||
}
|
}
|
||||||
|
|
||||||
const IS_APP = window.electron && window.electron.IS_APP || window.matchMedia('(display-mode: standalone)').matches;
|
const IS_APP = window.IS_APP || window.electron && window.electron.IS_APP || window.matchMedia('(display-mode: standalone)').matches;
|
||||||
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -24,12 +24,16 @@ class FaIcon extends LitElement
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.size = "1.1em";
|
this.size = "1.1em";
|
||||||
|
this.width = null;
|
||||||
|
this.height = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
svg: { type: String },
|
svg: { type: String },
|
||||||
size: { type: String }
|
size: { type: String },
|
||||||
|
width: { type: String },
|
||||||
|
height: { type: String }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +58,19 @@ class FaIcon extends LitElement
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = {width: this.size, height: this.size};
|
const styles = {};
|
||||||
|
|
||||||
|
if (this.size) {
|
||||||
|
styles.width = this.size;
|
||||||
|
styles.height = this.size;
|
||||||
|
} else {
|
||||||
|
if (this.width) {
|
||||||
|
styles.width = this.width;
|
||||||
|
}
|
||||||
|
if (this.height) {
|
||||||
|
styles.height = this.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return html`<svg style="${styleMap(styles)}"><g>${unsafeSVG(this.svg)}</g></svg>`;
|
return html`<svg style="${styleMap(styles)}"><g>${unsafeSVG(this.svg)}</g></svg>`;
|
||||||
}
|
}
|
||||||
|
|||||||
+180
-20
@@ -11,6 +11,7 @@ import "keyword-mark-element/lib/keyword-mark.js";
|
|||||||
import { getTS } from './pageutils';
|
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';
|
||||||
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -28,7 +29,10 @@ class Pages extends LitElement
|
|||||||
this.currList = 0;
|
this.currList = 0;
|
||||||
|
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
this.editable = false;
|
||||||
this.changeNeeded = false;
|
this.changeNeeded = false;
|
||||||
|
|
||||||
|
this.selectedPages = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get sortKeys() {
|
static get sortKeys() {
|
||||||
@@ -55,6 +59,10 @@ class Pages extends LitElement
|
|||||||
query: { type: String },
|
query: { type: String },
|
||||||
|
|
||||||
loading: { type: Boolean },
|
loading: { type: Boolean },
|
||||||
|
editable: { type: Boolean },
|
||||||
|
|
||||||
|
selectedPages: { type: Set },
|
||||||
|
allSelected: { type: Boolean }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +227,9 @@ class Pages extends LitElement
|
|||||||
flex: auto;
|
flex: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.selected {
|
||||||
|
background-color: ghostwhite;
|
||||||
|
}
|
||||||
.panel-block:last-child {
|
.panel-block:last-child {
|
||||||
border-bottom: 1px solid rgb(237, 237, 237);
|
border-bottom: 1px solid rgb(237, 237, 237);
|
||||||
}
|
}
|
||||||
@@ -227,8 +237,8 @@ class Pages extends LitElement
|
|||||||
border-left: 1px solid rgb(237, 237, 237);
|
border-left: 1px solid rgb(237, 237, 237);
|
||||||
border-right: 1px solid rgb(237, 237, 237);
|
border-right: 1px solid rgb(237, 237, 237);
|
||||||
}
|
}
|
||||||
.panel-block.status {
|
.status.level {
|
||||||
justify-content: space-between;
|
width: 100%;
|
||||||
}
|
}
|
||||||
nav.panel {
|
nav.panel {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -291,24 +301,67 @@ 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>
|
||||||
<div class="panel-block status">
|
<div class="panel-block">
|
||||||
<span class="num-results">${this.formatResults()}</span>
|
<div class="status level is-mobile">
|
||||||
|
<div class="level-left">
|
||||||
|
${this.editable ? html`
|
||||||
|
<div class="check-select level-item">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input @change=${this.onSelectAll} type="checkbox" .checked="${this.allSelected}">
|
||||||
|
</label>
|
||||||
|
</div>` : ``}
|
||||||
|
|
||||||
|
<span class="num-results level-item">${this.formatResults()}</span>
|
||||||
|
|
||||||
|
<div class="level-item dropdown is-hoverable">
|
||||||
|
<div class="dropdown-trigger">
|
||||||
|
<button 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">
|
||||||
|
${this.editable ? html`
|
||||||
|
<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>
|
||||||
|
|
||||||
${this.currList === 0 ? html`
|
${this.currList === 0 ? html`
|
||||||
|
<div class="level-right">
|
||||||
<wr-sorter id="pages"
|
<wr-sorter id="pages"
|
||||||
defaultKey="ts"
|
defaultKey="ts"
|
||||||
?defaultDesc="true"
|
?defaultDesc="true"
|
||||||
.sortKeys="${Pages.sortKeys}"
|
.sortKeys="${Pages.sortKeys}"
|
||||||
.data="${this.filteredPages}"
|
.data="${this.filteredPages}"
|
||||||
@sort-changed="${(e) => this.sortedPages = e.detail.sortedData}"
|
@sort-changed="${(e) => this.sortedPages = e.detail.sortedData}"
|
||||||
class="${this.filteredPages.length ? '' : 'is-hidden'}">
|
class="${this.filteredPages.length ? '' : 'is-hidden'} level-item">
|
||||||
</wr-sorter>` : ``}
|
</wr-sorter>` : ``}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</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="panel-block">
|
<div class="panel-block ${this.selectedPages.has(p.id) ? 'selected' : ''}">
|
||||||
<wr-page-entry @delete-page="${this.onDeletePage}" replayPrefix="${this.collInfo.replayPrefix}" query="${this.query}" .page="${p}">
|
<wr-page-entry .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}">
|
||||||
</wr-page-entry>
|
</wr-page-entry>
|
||||||
</div>`)}
|
</div>`)}
|
||||||
</div>
|
</div>
|
||||||
@@ -316,6 +369,44 @@ class Pages extends LitElement
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSelectToggle(event) {
|
||||||
|
const {page, selected} = event.detail;
|
||||||
|
if (selected) {
|
||||||
|
this.selectedPages.add(page);
|
||||||
|
} else {
|
||||||
|
this.selectedPages.delete(page);
|
||||||
|
}
|
||||||
|
this.allSelected = (this.selectedPages.size === this.sortedPages.length);
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectAll(event) {
|
||||||
|
this.allSelected = event.currentTarget.checked;
|
||||||
|
if (!this.allSelected) {
|
||||||
|
this.selectedPages.clear();
|
||||||
|
} else {
|
||||||
|
//this.selectedPages = new Set();
|
||||||
|
this.sortedPages.forEach(p => {
|
||||||
|
this.selectedPages.add(p.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//this.allSelected = (this.selectedPages.size === this.sortedPages.length);
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onDownload(event, format, selected) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.set("pages", selected ? Array.from(this.selectedPages.keys()).join(",") : "all");
|
||||||
|
params.set("format", format);
|
||||||
|
if (this.collInfo.filename) {
|
||||||
|
params.set("filename", this.collInfo.filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = `${this.collInfo.apiPrefix}/dl?` + params.toString();
|
||||||
|
}
|
||||||
|
|
||||||
async onDeletePage(event) {
|
async onDeletePage(event) {
|
||||||
const page = event.currentTarget.page;
|
const page = event.currentTarget.page;
|
||||||
const resp = await fetch(`${this.collInfo.apiPrefix}/page/${page.id}`, {method: 'DELETE'});
|
const resp = await fetch(`${this.collInfo.apiPrefix}/page/${page.id}`, {method: 'DELETE'});
|
||||||
@@ -339,7 +430,7 @@ class Pages extends LitElement
|
|||||||
|
|
||||||
formatResults() {
|
formatResults() {
|
||||||
if (!this.collInfo || !this.collInfo.pages.length) {
|
if (!this.collInfo || !this.collInfo.pages.length) {
|
||||||
return html`No Pages defined this archive. Check out <a href="#view=resources">Page Resources</a> to search by URL.`;
|
return html`No Pages defined this archive. Check out <a href="#view=resources">Page Resources</a> to search by URL.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.loading) {
|
if (this.loading) {
|
||||||
@@ -377,6 +468,8 @@ class PageEntry extends LitElement
|
|||||||
this.page = null;
|
this.page = null;
|
||||||
this.replayPrefix = "";
|
this.replayPrefix = "";
|
||||||
this.deleting = false;
|
this.deleting = false;
|
||||||
|
this.editable = false;
|
||||||
|
this.iconValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -385,7 +478,10 @@ class PageEntry extends LitElement
|
|||||||
textSnippet: { type: String },
|
textSnippet: { type: String },
|
||||||
page: { type: Object },
|
page: { type: Object },
|
||||||
replayPrefix: { type: String },
|
replayPrefix: { type: String },
|
||||||
deleting: { type: Boolean }
|
deleting: { type: Boolean },
|
||||||
|
selected: { type: Boolean },
|
||||||
|
editable: { type: Boolean },
|
||||||
|
iconValid: { type: Boolean }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,6 +492,19 @@ class PageEntry extends LitElement
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-select {
|
||||||
|
padding-right: 1.0em;
|
||||||
|
height: 100%;
|
||||||
|
margin: auto 0 auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.favicon {
|
.favicon {
|
||||||
@@ -423,10 +532,12 @@ class PageEntry extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 767px) {
|
@media screen and (max-width: 767px) {
|
||||||
|
.col-date {
|
||||||
|
margin-left: calc(24px + 1rem);
|
||||||
|
}
|
||||||
.col-date div {
|
.col-date div {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.columns {
|
.columns {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
@@ -438,24 +549,39 @@ class PageEntry extends LitElement
|
|||||||
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.updateFavIcon();
|
||||||
this.deleting = false;
|
this.deleting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
getDateTS() {
|
||||||
const p = this.page;
|
|
||||||
let date = null;
|
let date = null;
|
||||||
let ts = p.ts;
|
let ts = this.page.ts;
|
||||||
try {
|
try {
|
||||||
date = new Date(p.ts || p.date);
|
date = new Date(this.page.ts || this.page.date);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const timestamp = (date && !isNaN(date)) ? getTS(date.toISOString()) : "";
|
const timestamp = (date && !isNaN(date)) ? getTS(date.toISOString()) : "";
|
||||||
|
return {date, timestamp};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const p = this.page;
|
||||||
|
const {date, timestamp} = this.getDateTS();
|
||||||
|
|
||||||
const hasSize = typeof(p.size) === "number";
|
const hasSize = typeof(p.size) === "number";
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
${this.editable ? html`
|
||||||
|
<div class="check-select">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input @change=${this.onSendSelToggle} type="checkbox" .checked="${this.selected}">
|
||||||
|
</label>
|
||||||
|
</div>` : ``}
|
||||||
|
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-date is-2">
|
<div class="column col-date is-2">
|
||||||
<div>${date ? date.toLocaleDateString() : ""}</div>
|
<div>${date ? date.toLocaleDateString() : ""}</div>
|
||||||
@@ -465,8 +591,8 @@ class PageEntry extends LitElement
|
|||||||
<article class="media">
|
<article class="media">
|
||||||
<figure class="media-left">
|
<figure class="media-left">
|
||||||
<p class="">
|
<p class="">
|
||||||
${p.favIconUrl ? html`
|
${this.iconValid ? html`
|
||||||
<img class="favicon" src="${this.replayPrefix}/id_/${p.favIconUrl}"/>` : html`
|
<img class="favicon" @error="${(e) => this.iconValid = false}" src="${this.replayPrefix}/${timestamp}id_/${p.favIconUrl}"/>` : html`
|
||||||
<span class="favicon"></span>`}
|
<span class="favicon"></span>`}
|
||||||
</p>
|
</p>
|
||||||
</figure>
|
</figure>
|
||||||
@@ -487,11 +613,39 @@ class PageEntry extends LitElement
|
|||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${hasSize ? html`
|
|
||||||
|
${this.editable ? html`
|
||||||
${!this.deleting ? html`
|
${!this.deleting ? html`
|
||||||
<button @click="${this.onSendDeletePage}" class="delete"></button>` : html`
|
<button @click="${this.onSendDeletePage}" class="delete"></button>` : html`
|
||||||
<button class="button delete is-loading is-static"></button>
|
<button class="button delete is-loading is-static"></button>
|
||||||
`}` : ''}`;
|
`}` : ''}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateFavIcon() {
|
||||||
|
if (!this.page.favIconUrl) {
|
||||||
|
this.favIconData = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {timestamp} = this.getDateTS();
|
||||||
|
|
||||||
|
const resp = await fetch(`${this.replayPrefix}/${timestamp}id_/${this.page.favIconUrl}`);
|
||||||
|
|
||||||
|
if (resp.status != 200) {
|
||||||
|
this.favIconData = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = await resp.arrayBuffer();
|
||||||
|
const mime = resp.headers.get("content-type");
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.favIconData = `data:${mime};base64,${btoa(String.fromCharCode.apply(null, payload))}`;
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
this.favIconData = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSnippet() {
|
updateSnippet() {
|
||||||
@@ -558,6 +712,12 @@ class PageEntry extends LitElement
|
|||||||
this.deleting = true;
|
this.deleting = true;
|
||||||
this.dispatchEvent(new CustomEvent("delete-page", {detail: {page}}));
|
this.dispatchEvent(new CustomEvent("delete-page", {detail: {page}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSendSelToggle(event) {
|
||||||
|
const page = this.page.id;
|
||||||
|
const selected = event.currentTarget.checked;
|
||||||
|
this.dispatchEvent(new CustomEvent("sel-page", {detail: {page, selected}}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("wr-page-view", Pages);
|
customElements.define("wr-page-view", Pages);
|
||||||
|
|||||||
Reference in New Issue
Block a user