diff --git a/assets/main.scss b/assets/main.scss index 53d149d1..63bac8e6 100644 --- a/assets/main.scss +++ b/assets/main.scss @@ -134,3 +134,16 @@ a:focus { box-sizing: border-box; overflow: hidden; } + +// Make available to screen readers while hiding element visually: +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} \ No newline at end of file diff --git a/package.json b/package.json index 82bcfa00..638299e9 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "stream-browserify": "^3.0.0" }, "devDependencies": { + "@types/flexsearch": "^0.7.3", "copy-webpack-plugin": "^9.0.1", "css-loader": "^6.2.0", "electron": "^25.1.1", @@ -177,5 +178,8 @@ }, "lint-staged": { "*.ts": "prettier --write" + }, + "prettier": { + "trailingComma": "all" } } diff --git a/src/pageentry.js b/src/pageentry.ts similarity index 79% rename from src/pageentry.js rename to src/pageentry.ts index 5514c385..a477a8d8 100644 --- a/src/pageentry.js +++ b/src/pageentry.ts @@ -1,6 +1,7 @@ import prettyBytes from "pretty-bytes"; import { LitElement, html, css, unsafeCSS } from "lit"; +import { property, state } from "lit/decorators.js"; import "keyword-mark-element/lib/keyword-mark.js"; @@ -10,35 +11,41 @@ import { wrapCss } from "./misc"; // =========================================================================== class PageEntry extends LitElement { - constructor() { - super(); - this.query = ""; - this.textSnippet = ""; - this.page = null; - this.replayPrefix = ""; - this.deleting = false; - this.editable = false; - this.iconValid = false; - this.index = 0; - this.isCurrent = false; - this.isSidebar = false; - } + @property({ type: String }) + query = ""; - static get properties() { - return { - query: { type: String }, - textSnippet: { type: String }, - page: { type: Object }, - replayPrefix: { type: String }, - deleting: { type: Boolean }, - selected: { type: Boolean }, - editable: { type: Boolean }, - iconValid: { type: Boolean }, - index: { type: Number }, - isCurrent: { type: Boolean }, - isSidebar: { type: Boolean }, - }; - } + @property({ type: String }) + textSnippet: string | null = ""; + + @property({ type: Object }) + page: any = null; + + @property({ type: String }) + replayPrefix = ""; + + @property({ type: Boolean }) + deleting = false; + + @property({ type: Boolean }) + selected = false; + + @property({ type: Boolean }) + editable = false; + + @property({ type: Number }) + index = 0; + + @property({ type: Boolean }) + isCurrent = false; + + @property({ type: Boolean }) + isSidebar = false; + + @state() + thumbnailValid = true; + + @state() + iconValid = true; static get styles() { return wrapCss(css` @@ -79,17 +86,28 @@ class PageEntry extends LitElement { } .favicon { - width: 24px !important; - height: 24px !important; display: inline-block; vertical-align: text-bottom; } - img.favicon { + + .media-left .favicon { + width: 2rem; + height: 2rem; + } + .media-left img.favicon { filter: drop-shadow(1px 1px 2px grey); } + .media-content .favicon { + width: 1.15rem; + height: 1.15rem; + margin: 0 0.25rem; + } + .media-left { + width: 6rem; align-self: center; + text-align: center; } .delete-button { @@ -148,10 +166,7 @@ class PageEntry extends LitElement { display: inline; } ${prefix} .col-index { - position: absolute; - top: 0px; - left: 0px; - margin-top: -0.75em; + display: none; } ${prefix} .columns { display: flex; @@ -161,14 +176,15 @@ class PageEntry extends LitElement { display: initial !important; font-style: italic; } + ${prefix} .media-left { + padding-left: 0.75rem; + } `; } updated(changedProperties) { if (changedProperties.has("page") || changedProperties.has("query")) { this.updateSnippet(); - this.iconValid = !!this.page.favIconUrl; - //this.updateFavIcon(); this.deleting = false; } } @@ -206,18 +222,7 @@ class PageEntry extends LitElement {
${date ? date.toLocaleString() : ""}
@@ -276,32 +281,28 @@ class PageEntry extends LitElement {
`;
}
- async updateFavIcon() {
- if (!this.page.favIconUrl) {
- this.favIconData = null;
- return;
+ private renderPageIcon() {
+ if (!this.thumbnailValid) {
+ return this.renderFavicon();
}
+ return html` (this.thumbnailValid = false)}
+ src=${`${this.replayPrefix}/${this.page.timestamp}id_/urn:thumbnail:${this.page.url}`}
+ loading="lazy"
+ />`;
+ }
- const resp = await fetch(
- `${this.replayPrefix}/${this.page.timestamp}id_/${this.page.favIconUrl}`,
- );
-
- if (resp.status != 200) {
- this.favIconData = null;
+ private renderFavicon() {
+ if (!this.iconValid || !this.page.favIconUrl) {
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;
- }
+ return html` (this.iconValid = false)}
+ src=${`${this.replayPrefix}/${this.page.timestamp}id_/${this.page.favIconUrl}`}
+ loading="lazy"
+ />`;
}
updateSnippet() {
diff --git a/src/pages.js b/src/pages.ts
similarity index 90%
rename from src/pages.js
rename to src/pages.ts
index 762e510a..44f19ac0 100644
--- a/src/pages.js
+++ b/src/pages.ts
@@ -1,6 +1,7 @@
"use strict";
import { LitElement, html, css, unsafeCSS } from "lit";
+import { property, state } from "lit/decorators.js";
import { wrapCss, clickOnSpacebarPress } from "./misc";
import ndjson from "fetch-ndjson";
@@ -11,47 +12,95 @@ import { getTS, getPageDateTS } from "./pageutils";
import fasSearch from "@fortawesome/fontawesome-free/svgs/solid/search.svg";
import fasAngleDown from "@fortawesome/fontawesome-free/svgs/solid/angle-down.svg";
-
import fasEdit from "@fortawesome/fontawesome-free/svgs/solid/edit.svg";
+import type { Sorter } from "./sorter";
+import type { PageEntry } from "./pageentry";
+
// ===========================================================================
class Pages extends LitElement {
- constructor() {
- super();
- this.filteredPages = [];
- this.sortedPages = [];
- this.query = "";
- this.flex = null;
- this.textPages = null;
- this.newQuery = null;
- this.loading = false;
- this.updatingSearch = false;
+ @property({ type: Array })
+ filteredPages: any[] = [];
- this.showAllPages = false;
- this.hasExtraPages = false;
+ @property({ type: Array })
+ sortedPages: any[] = [];
- this.currList = 0;
+ @property({ type: String })
+ query = "";
- this.active = false;
- this.editable = false;
- this.changeNeeded = false;
+ @property()
+ flex: any = null;
- this.selectedPages = new Set();
+ @property()
+ textPages: any = null;
- this.menuActive = false;
+ @property()
+ newQuery: any = null;
- this.sortKey = "date";
- this.sortDesc = true;
+ @property({ type: Boolean })
+ loading = false;
- this.isSidebar = false;
- this.url = "";
- this.ts = "";
+ @property({ type: Boolean })
+ updatingSearch = false;
- this.editing = false;
+ @property({ type: Boolean })
+ showAllPages = false;
- this.toDeletePages = null;
- this.toDeletePage = null;
- }
+ @property({ type: Boolean })
+ hasExtraPages = false;
+
+ @property({ type: Number })
+ currList: number = 0;
+
+ @property({ type: Boolean })
+ active = false;
+
+ @property({ type: Boolean })
+ editable = false;
+
+ @property({ type: Boolean })
+ changeNeeded = false;
+
+ @property({ type: Set })
+ selectedPages = new Set();
+
+ @property({ type: Boolean })
+ menuActive = false;
+
+ @property({ type: String })
+ sortKey = "date";
+
+ @property({ type: Boolean })
+ sortDesc = true;
+
+ @property({ type: Boolean })
+ isSidebar = false;
+
+ @property({ type: String })
+ url = "";
+
+ @property({ type: String })
+ ts = "";
+
+ @property({ type: Boolean })
+ editing: any = false;
+
+ @property({ type: Object })
+ toDeletePages: any = null;
+
+ @property({ type: Object })
+ toDeletePage: any = null;
+
+ @property({ type: Object })
+ collInfo: any;
+
+ @property({ type: Boolean })
+ allSelected = false;
+
+ @property({ type: String })
+ defaultKey = "";
+
+ private _ival: any;
static get sortKeys() {
return [
@@ -70,42 +119,6 @@ class Pages extends LitElement {
];
}
- static get properties() {
- return {
- active: { type: Boolean },
- collInfo: { type: Object },
- currList: { type: Number },
- filteredPages: { type: Array },
- sortedPages: { type: Array },
-
- showAllPages: { type: Boolean },
-
- query: { type: String },
- defaultKey: { type: String },
-
- loading: { type: Boolean },
- updatingSearch: { type: Boolean },
- editable: { type: Boolean },
-
- selectedPages: { type: Set },
- allSelected: { type: Boolean },
-
- menuActive: { type: Boolean },
-
- sortKey: { type: String },
- sortDesc: { type: Boolean },
-
- isSidebar: { type: Boolean },
- url: { type: String },
- ts: { type: String },
-
- editing: { type: Boolean },
-
- toDeletePages: { type: Object },
- toDeletePage: { type: Object },
- };
- }
-
_timedUpdate() {
if (this.newQuery !== null) {
this.query = this.newQuery;
@@ -114,7 +127,7 @@ class Pages extends LitElement {
}
}
- async updated(changedProperties) {
+ async updated(changedProperties: Map