From 79d2f1585c9d9fe82e24db5ac933b36d97f14788 Mon Sep 17 00:00:00 2001
From: sua yoo
Date: Thu, 7 Sep 2023 10:42:21 -0700
Subject: [PATCH] Show snapshot timestamp in location bar (#228)
- Shows how many snapshots are available per page in URL bar
- Allows users to navigate through snapshots through timestamp dropdown
- Refactors coll.js and pageutils to typescript
---
package.json | 2 +-
src/{coll.js => coll.ts} | 465 ++++++++++++++++++++---------
src/{pageutils.js => pageutils.ts} | 15 +-
src/shoelace.js | 1 -
src/shoelace.ts | 4 +
yarn.lock | 18 +-
6 files changed, 353 insertions(+), 152 deletions(-)
rename src/{coll.js => coll.ts} (81%)
rename src/{pageutils.js => pageutils.ts} (87%)
delete mode 100644 src/shoelace.js
create mode 100644 src/shoelace.ts
diff --git a/package.json b/package.json
index 6a1d3df..01ff73f 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"license": "AGPL-3.0-or-later",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.4",
- "@shoelace-style/shoelace": "^2.7.0",
+ "@shoelace-style/shoelace": "^2.8.0",
"@webrecorder/wabac": "^2.16.12",
"bulma": "^0.9.3",
"electron-log": "^4.4.1",
diff --git a/src/coll.js b/src/coll.ts
similarity index 81%
rename from src/coll.js
rename to src/coll.ts
index d346e9a..c1ccb4e 100644
--- a/src/coll.js
+++ b/src/coll.ts
@@ -1,4 +1,11 @@
import { LitElement, html, css } from "lit";
+import { property, state } from "lit/decorators.js";
+import { map } from "lit/directives/map.js";
+import type {
+ SlDropdown,
+ SlMenu,
+ SlSelectEvent,
+} from "@shoelace-style/shoelace";
import {
wrapCss,
rwpLogo,
@@ -9,7 +16,12 @@ import {
replayPrefix,
} from "./misc";
-import { sourceToId, tsToDate, getPageDateTS } from "./pageutils";
+import {
+ sourceToId,
+ tsToDate,
+ getPageDateTS,
+ getDateFromTS,
+} from "./pageutils";
import fasBook from "@fortawesome/fontawesome-free/svgs/solid/book.svg";
@@ -31,112 +43,122 @@ import fasMenuV from "@fortawesome/fontawesome-free/svgs/solid/ellipsis-v.svg";
import fasAngleLeft from "@fortawesome/fontawesome-free/svgs/solid/angle-left.svg";
import fasAngleRight from "@fortawesome/fontawesome-free/svgs/solid/angle-right.svg";
+import fasCaretDown from "@fortawesome/fontawesome-free/svgs/solid/caret-down.svg";
import { RWPEmbedReceipt } from "./embed-receipt.js";
import Split from "split.js";
+import type { Replay } from "./replay";
const RWP_SCHEME = "search://";
// ===========================================================================
class Coll extends LitElement {
+ @property({ type: Boolean })
+ inited = false;
+
+ @property({ type: String })
+ sourceUrl: string | null = null;
+
+ @property({ type: Object, attribute: false })
+ loadInfo: any;
+
+ @property({ type: Boolean })
+ showSidebar: boolean | null = null;
+
+ @property({ type: Object, attribute: false })
+ collInfo: any = null;
+
+ @property({ type: String })
+ coll = "";
+
+ @property({ type: Boolean })
+ hasStory = false;
+
+ @property({ type: Boolean })
+ isLoading = false;
+
+ @property({ type: Object, attribute: false })
+ tabData: {
+ view?: string;
+ url?: string;
+ ts?: string;
+ multiTs?: string[];
+ currList?: number;
+ query?: string;
+ urlSearchType?: string;
+ currMime?: string;
+ } = {};
+
+ @property({ type: String })
+ url = "";
+
+ @property({ type: String })
+ ts = "";
+
+ @property({ type: Boolean })
+ isFullscreen: any;
+
+ @property({ type: Boolean })
+ menuActive = false;
+
+ @property({ type: String })
+ embed: any;
+
+ @property({ type: Boolean })
+ embedDropdownActive = false;
+
+ @property({ type: Boolean })
+ editable = false;
+
+ @property({ type: Boolean })
+ browsable = true;
+
+ @property({ type: Boolean })
+ clearable = true;
+
+ @property({ type: Boolean })
+ isVisible = true;
+
+ @property({ type: String })
+ favIconUrl = "";
+
+ @property({ type: String })
+ appName = "ReplayWeb.page";
+
+ @property({ type: String })
+ appVersion = VERSION;
+
+ @property({ type: String })
+ appLogo = rwpLogo;
+
+ @property({ type: Number })
+ autoUpdateInterval = 10;
+
+ @property({ type: String })
+ swName: any;
+
+ private tabNames = ["pages", "story", "resources", "info"];
+ private tabLabels = {
+ pages: "Pages",
+ story: "Story",
+ resources: "URLs",
+ info: "Archive Info",
+ };
+ private splitter: any = null;
+ private _replaceLoc = false;
+ private _locUpdateNeeded = false;
+ private _locationHash = "";
+ private _autoUpdater: Promise | null = null;
+ private observer?: IntersectionObserver;
+
constructor() {
super();
- this.sourceUrl = null;
- this.inited = false;
- this.isLoading = false;
-
- this.coll = "";
-
- this.collInfo = null;
-
- this._replaceLoc = false;
- this._locUpdateNeeded = false;
-
- this._locationHash = "";
-
- this.tabData = {};
- this.url = "";
- this.ts = "";
-
- this.tabNames = ["pages", "story", "resources", "info"];
- this.tabLabels = {
- pages: "Pages",
- story: "Story",
- resources: "URLs",
- info: "Archive Info",
- };
-
- this.menuActive = false;
- this.embedDropdownActive = false;
-
- this.hasStory = false;
-
- this.editable = false;
- this.browsable = true;
- this.clearable = true;
-
this.showSidebar = localStorage.getItem("pages:showSidebar") === "1";
- this.splitter = null;
-
- this.isVisible = true;
-
- this.favIconUrl = "";
-
- this.autoUpdateInterval = 10;
- this._autoUpdater = null;
-
- this.appName = "ReplayWeb.page";
- this.appVersion = VERSION;
- this.appLogo = rwpLogo;
- }
-
- static get properties() {
- return {
- inited: { type: Boolean },
-
- sourceUrl: { type: String },
- loadInfo: { type: Object, attribute: false },
-
- showSidebar: { type: Boolean },
-
- collInfo: { type: Object, attribute: false },
- coll: { type: String },
-
- hasStory: { type: Boolean },
- isLoading: { type: Boolean },
-
- tabData: { type: Object, attribute: false },
-
- url: { type: String },
- ts: { type: String },
-
- isFullscreen: { type: Boolean },
- menuActive: { type: Boolean },
-
- embed: { type: String },
- embedDropdownActive: { type: Boolean },
-
- editable: { type: Boolean },
- browsable: { type: Boolean },
- clearable: { type: Boolean },
-
- isVisible: { type: Boolean },
-
- favIconUrl: { type: String },
-
- appName: { type: String },
- appVersion: { type: String },
- appLogo: { type: String },
-
- autoUpdateInterval: { type: Number },
-
- swName: { type: String },
- };
}
firstUpdated() {
this.inited = true;
- window.addEventListener("hashchange", (event) => this.onHashChange(event));
+ window.addEventListener("hashchange", (event) => this.onHashChange());
this.addEventListener("fullscreenchange", () => {
this.isFullscreen = !!document.fullscreenElement;
@@ -171,6 +193,43 @@ class Coll extends LitElement {
}
}
+ async getMultiTimestamps(): Promise {
+ if (!this.tabData.url) return;
+ const resp = await fetch(
+ apiPrefix +
+ "/c/" +
+ this.coll +
+ "/ts/?url=" +
+ window.encodeURIComponent(this.tabData.url),
+ );
+ if (resp.status !== 200) {
+ return;
+ }
+ const json = await resp.json();
+ this.updateTabData({ multiTs: json.timestamps });
+ }
+
+ willUpdate(changedProperties: Map) {
+ if (changedProperties.has("tabData") && this.tabData) {
+ // Format tab data from URL query params
+ const tabData = {};
+ Object.entries(this.tabData).forEach(([key, value]) => {
+ if (!value) return;
+ if (key === "multiTs" && typeof value === "string") {
+ tabData[key] = value.split(",");
+ } else {
+ tabData[key] = value;
+ }
+ });
+ this.tabData = tabData;
+
+ const prevTabData = changedProperties.get("tabData");
+ if (this.tabData.url && this.tabData.url !== prevTabData?.url) {
+ this.getMultiTimestamps();
+ }
+ }
+ }
+
updated(changedProperties) {
// if (changedProperties.has("url") || changedProperties.has("ts")) {
// if (this.url.startsWith("rwp?")) {
@@ -192,13 +251,7 @@ class Coll extends LitElement {
if (!this.collInfo || !this.collInfo.coll) {
return;
}
-
- // don't add empty params to shorten query
- Object.keys(this.tabData).forEach(
- (key) => !this.tabData[key] && delete this.tabData[key],
- );
-
- const newHash = "#" + new URLSearchParams(this.tabData).toString();
+ const newHash = "#" + new URLSearchParams(this.tabData as any).toString();
if (!this.tabData.url) {
this.url =
@@ -206,6 +259,7 @@ class Coll extends LitElement {
}
if (newHash !== this._locationHash) {
this._locationHash = newHash;
+
if (
this._replaceLoc ||
Object.keys(changedProperties.get("tabData")).length === 0
@@ -217,7 +271,9 @@ class Coll extends LitElement {
} else {
window.location.hash = this._locationHash;
if (!this.showSidebar) {
- const replay = this.renderRoot.querySelector("wr-coll-replay");
+ const replay = this.renderRoot.querySelector(
+ "wr-coll-replay",
+ ) as Replay;
if (replay) {
replay.focus();
}
@@ -246,7 +302,7 @@ class Coll extends LitElement {
configureSplitter() {
if (this.tabData.url && this.showSidebar) {
const contents = this.renderRoot.querySelector("#contents");
- const replay = this.renderRoot.querySelector("wr-coll-replay");
+ const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay;
if (contents && replay && !this.splitter) {
const opts = {
@@ -265,7 +321,7 @@ class Coll extends LitElement {
},
};
- this.splitter = Split([contents, replay], opts);
+ this.splitter = Split([contents as any, replay], opts);
}
} else if (this.splitter) {
try {
@@ -378,7 +434,10 @@ class Coll extends LitElement {
this._locationHash = hash;
}
- if (this.collInfo.coll && !this.tabNames.includes(this.tabData.view)) {
+ if (
+ this.collInfo.coll &&
+ (!this.tabData.view || !this.tabNames.includes(this.tabData.view))
+ ) {
const view = this.hasStory
? "story"
: this.editable || this.collInfo.pages.length
@@ -427,9 +486,15 @@ class Coll extends LitElement {
event.target.id === this.tabData.view ||
(event.target.id === "replay" && this.tabData.url)
) {
- this.updateTabData(event.detail.data, event.detail.replaceLoc, false);
+ this.updateTabData(
+ event.detail.data,
+ event.detail.replaceLoc /*, false */,
+ );
} else if (this.showSidebar && this.tabData.url) {
- this.updateTabData(event.detail.data, event.detail.replaceLoc, true);
+ this.updateTabData(
+ event.detail.data,
+ event.detail.replaceLoc /*, true */,
+ );
}
}
@@ -575,17 +640,69 @@ class Coll extends LitElement {
#datetime {
position: absolute;
- right: 1em;
+ right: 0.5rem;
z-index: 10;
+ background: #fff;
+ top: 1px;
+ bottom: 1px;
+ display: flex;
+ align-items: center;
+ line-height: 2;
+ }
+
+ /* Gradient to indicate URL clipping */
+ #datetime:before {
+ content: "";
+ position: absolute;
+ top: 0;
+ width: 2em;
+ height: 100%;
+ transform: translateX(-100%);
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0),
- #fff 15%,
+ #fff 50%,
#fff
);
- margin: -35px 0 0 0px;
- padding-left: 3em;
- line-height: 2;
+ pointer-events: none;
+ }
+
+ .timestamp-dropdown-btn {
+ all: unset;
+ cursor: pointer;
+ display: flex;
+ gap: var(--sl-spacing-x-small);
+ align-items: center;
+ transition: background-color var(--sl-transition-fast);
+ color: var(--sl-color-neutral-600);
+ }
+
+ .timestamp-dropdown-btn:hover {
+ color: var(--sl-color-neutral-900);
+ }
+
+ .timestamp-dropdown-btn:hover .timestamp-count-badge {
+ background-color: var(--sl-color-blue-600);
+ }
+
+ .timestamp-count-badge {
+ display: inline-flex;
+ gap: var(--sl-spacing-2x-small);
+ background-color: var(--sl-color-blue-500);
+ color: var(--sl-color-neutral-0);
+ line-height: 1;
+ padding: var(--sl-spacing-3x-small) var(--sl-spacing-x-small);
+ border-radius: var(--sl-border-radius-small);
+ transition: background-color var(--sl-transition-fast);
+ }
+
+ .timestamp-count {
+ font-weight: 600;
+ transform: translateY(0.075em);
+ }
+
+ .timestamp-menu-item[aria-selected="true"]::part(label) {
+ color: var(--sl-color-blue-600);
}
.menu-head {
@@ -720,7 +837,7 @@ class Coll extends LitElement {
if (!isReplay && this.tabData && this.tabData.view) {
const detail = {
- title: this.tabLabels[this.tabData.view],
+ title: (this.tabLabels as any)[this.tabData.view],
replayTitle: false,
};
this.dispatchEvent(
@@ -738,7 +855,7 @@ class Coll extends LitElement {
embed="${this.embed}"
swName="${this.swName}"
.coll="${this.coll}"
- .sourceUrl="${this.sourceUrl}"
+ sourceUrl="${this.sourceUrl || ""}"
@coll-loaded=${this.onCollLoaded}
>`;
} else if (this.collInfo) {
@@ -752,7 +869,7 @@ class Coll extends LitElement {
: isReplay
? "is-hidden"
: "full-pages"}"
- role="${isSidebar ? "complementary" : ""}"
+ role="${(isSidebar ? "complementary" : "") as any}"
aria-label="${isSidebar ? "Browse Contents" : ""}"
>
${this.renderTabHeader(isSidebar)}
@@ -765,7 +882,7 @@ class Coll extends LitElement {
role="main"
tabindex="-1"
.collInfo="${this.collInfo}"
- sourceUrl="${this.sourceUrl}"
+ sourceUrl="${this.sourceUrl || ""}"
url="${this.tabData.url || ""}"
ts="${this.tabData.ts || ""}"
@coll-tab-nav="${this.onCollTabNav}"
@@ -822,9 +939,9 @@ class Coll extends LitElement {
href="#story"
class="is-size-6"
aria-label="Story"
- aria-current="${this.tabData.view === "story"
+ aria-current="${(this.tabData.view === "story"
? "location"
- : ""}"
+ : "") as any}"
>
`
: ""}
- ${this.renderExtraToolbar(false)}
+ ${this.renderExtraToolbar()}
`;
}
+ private renderTimestamp() {
+ const timestampStrs: { date: string; label: string }[] = [];
+ this.tabData.multiTs?.forEach((ts) => {
+ // Filter out invalid dates
+ try {
+ const date = getDateFromTS(+ts);
+ const dateStr = tsToDate(date).toLocaleString();
+ timestampStrs.push({
+ date,
+ label: dateStr,
+ });
+ } catch {}
+ });
+ const currDateStr = tsToDate(this.ts).toLocaleString();
+ return html`
+ ${timestampStrs.length > 1
+ ? html`
+
+
+
+ ${timestampStrs.map(({ date: ts, label }) => {
+ const selected = this.ts === ts;
+ return html``;
+ })}
+
+
+ `
+ : currDateStr}
+
`;
+ }
+
renderVerifyInfo() {
if (this.embed !== "replay-with-info") {
return "";
@@ -1286,14 +1451,14 @@ class Coll extends LitElement {
}
dragStart() {
- const replay = this.renderRoot.querySelector("wr-coll-replay");
+ const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay;
if (replay) {
replay.setDisablePointer(true);
}
}
dragEnd() {
- const replay = this.renderRoot.querySelector("wr-coll-replay");
+ const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay;
if (replay) {
replay.setDisablePointer(false);
}
@@ -1332,7 +1497,7 @@ class Coll extends LitElement {
id="story"
.isSidebar="${isSidebar}"
class="${isStory ? "" : "is-hidden"} ${isSidebar ? "sidebar" : ""}"
- role="${isSidebar ? "" : "main"}"
+ role="${(isSidebar ? "" : "main") as any}"
>
`
: ""}
@@ -1349,7 +1514,7 @@ class Coll extends LitElement {
class="is-paddingless ${isResources ? "" : "is-hidden"} ${isSidebar
? "sidebar"
: ""}"
- role="${isSidebar ? "" : "main"}"
+ role="${(isSidebar ? "" : "main") as any}"
>
`
: ""}
@@ -1367,7 +1532,7 @@ class Coll extends LitElement {
id="pages"
@coll-update="${this.onCollUpdate}"
class="${isPages ? "" : "is-hidden"} ${isSidebar ? "sidebar" : ""}"
- role="${isSidebar ? "" : "main"}"
+ role="${(isSidebar ? "" : "main") as any}"
>
`
: ""}
@@ -1378,7 +1543,9 @@ class Coll extends LitElement {
// This is a workaround, since this app's routing doesn't permit normal
// following of in-page anchors.
event.preventDefault();
- this.renderRoot.querySelector("#skip-replay-target").focus();
+ (
+ this.renderRoot.querySelector("#skip-replay-target") as HTMLElement
+ ).focus();
}
onKeyDown(event) {
@@ -1496,7 +1663,7 @@ class Coll extends LitElement {
onSubmit(event) {
event.preventDefault();
- const input = this.renderRoot.querySelector("input");
+ const input = this.renderRoot.querySelector("input")!;
if (input.value) {
this.navigateTo(input.value);
} else {
@@ -1511,6 +1678,24 @@ class Coll extends LitElement {
}
}
+ onTimestampDropdownBtnBlur(event: MouseEvent) {
+ const btn = event.currentTarget as HTMLButtonElement;
+ const dropdown = btn.closest("sl-dropdown") as SlDropdown;
+ const relatedTarget = event.relatedTarget as HTMLElement;
+ if (relatedTarget && dropdown.contains(relatedTarget)) {
+ return;
+ }
+ if (dropdown.open) {
+ dropdown.hide();
+ }
+ }
+
+ onSelectTimestamp(event: SlSelectEvent) {
+ const menu = event.currentTarget as SlMenu;
+ const { item } = event.detail;
+ this.updateTabData({ ts: item.value });
+ }
+
navigateTo(value) {
let data;
@@ -1518,7 +1703,9 @@ class Coll extends LitElement {
data = { url: value };
if (value === this.tabData.url) {
- const replay = this.renderRoot.querySelector("wr-coll-replay");
+ const replay = this.renderRoot.querySelector(
+ "wr-coll-replay",
+ ) as Replay;
if (replay) {
replay.refresh();
}
@@ -1536,7 +1723,7 @@ class Coll extends LitElement {
_stringToParams(value) {
const q = new URLSearchParams(value.slice(RWP_SCHEME.length));
- const data = {};
+ const data: any = {};
data.url = "";
data.ts = "";
@@ -1581,7 +1768,7 @@ class Coll extends LitElement {
this.menuActive = false;
if (this.tabData.url) {
- const replay = this.renderRoot.querySelector("wr-coll-replay");
+ const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay;
if (replay) {
replay.refresh();
}
diff --git a/src/pageutils.js b/src/pageutils.ts
similarity index 87%
rename from src/pageutils.js
rename to src/pageutils.ts
index 222309f..e11da8a 100644
--- a/src/pageutils.js
+++ b/src/pageutils.ts
@@ -36,16 +36,26 @@ function tsToDate(ts) {
return new Date(datestr);
}
+// ===========================================================================
+function getDateFromTS(ts: number) {
+ let date: Date | null = null;
+ date = new Date(ts);
+ const timestamp =
+ date && date instanceof Date ? getTS(date.toISOString()) : "";
+ return timestamp;
+}
+
// ===========================================================================
function getPageDateTS(page) {
- let date = null;
+ let date: Date | null = null;
try {
date = new Date(page.ts || page.date);
} catch (e) {
// leave date unchanged in case of error
}
- const timestamp = date && !isNaN(date) ? getTS(date.toISOString()) : "";
+ const timestamp =
+ date && date instanceof Date ? getTS(date.toISOString()) : "";
return { date, timestamp };
}
@@ -114,6 +124,7 @@ export {
tsToDate,
getTS,
getPageDateTS,
+ getDateFromTS,
getReplayLink,
sourceToId,
parseURLSchemeHostPath,
diff --git a/src/shoelace.js b/src/shoelace.js
deleted file mode 100644
index fdb67e6..0000000
--- a/src/shoelace.js
+++ /dev/null
@@ -1 +0,0 @@
-// Cherry-picked Shoelace components:
diff --git a/src/shoelace.ts b/src/shoelace.ts
new file mode 100644
index 0000000..4ca779c
--- /dev/null
+++ b/src/shoelace.ts
@@ -0,0 +1,4 @@
+// Cherry-picked Shoelace components:
+import "@shoelace-style/shoelace/dist/components/dropdown/dropdown.js";
+import "@shoelace-style/shoelace/dist/components/menu/menu.js";
+import "@shoelace-style/shoelace/dist/components/menu-item/menu-item.js";
diff --git a/yarn.lock b/yarn.lock
index 4a54e99..ecdda29 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -153,10 +153,10 @@
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
-"@lit-labs/react@^1.1.1":
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/@lit-labs/react/-/react-1.2.1.tgz#5b421502cdf68a3639dec431318eeed2285f1c0e"
- integrity sha512-DiZdJYFU0tBbdQkfwwRSwYyI/mcWkg3sWesKRsHUd4G+NekTmmeq9fzsurvcKTNVa0comNljwtg4Hvi1ds3V+A==
+"@lit-labs/react@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@lit-labs/react/-/react-2.0.1.tgz#a670d4a6f3cb39393af787189e02da2803f61cd0"
+ integrity sha512-Nj+XB3HamqaWefN91lpFPJaqjJ78XzGkPWCedB4jyH22GBFEenpE9A/h8B/2dnIGXtNtd9D/RFpUdQ/dBtWFqA==
"@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0":
version "1.1.1"
@@ -374,14 +374,14 @@
resolved "https://registry.yarnpkg.com/@shoelace-style/localize/-/localize-3.1.1.tgz#f5b96e35a9a8709aa46d1aaa2359069c0db71534"
integrity sha512-NkM/hj3Js6yXCU9WxhsyxRUdyqUUUl/BSvIluUMptQteUWGOJaoyP1iMbOMqO544DYMzBfnoCw66ZHkGuTdKgA==
-"@shoelace-style/shoelace@^2.7.0":
- version "2.7.0"
- resolved "https://registry.yarnpkg.com/@shoelace-style/shoelace/-/shoelace-2.7.0.tgz#3844488cc1d5de4dfa966949961cb6faf4c2b35a"
- integrity sha512-w7FnHqBoYR6Ne42WZPW+bIJGIAGsUZgxF3WA9LLfqn3+Q4NOQ4r5jl/DEfsQmRUTmY0nUrs4WGO1CeTrhv6taA==
+"@shoelace-style/shoelace@^2.8.0":
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/@shoelace-style/shoelace/-/shoelace-2.8.0.tgz#995377abcff7d055c0c07dab63d97460453546ad"
+ integrity sha512-WigVUaW+MptefOW4zUlZaq+zn0n2hP+I4/mztoeJii5u3ex1CGexfQGcdw2gx6d7P7LAa6/NW0TlgAELzJQnCA==
dependencies:
"@ctrl/tinycolor" "^3.5.0"
"@floating-ui/dom" "^1.2.1"
- "@lit-labs/react" "^1.1.1"
+ "@lit-labs/react" "^2.0.1"
"@shoelace-style/animations" "^1.1.0"
"@shoelace-style/localize" "^3.1.1"
composed-offset-position "^0.0.4"