Add the tour logic, introduction and homepage tours.
This commit is contained in:
parent
8c62714b9b
commit
97cb3a5603
|
@ -0,0 +1,11 @@
|
|||
import {homepageSteps, homepageEventHandlers} from "./interface/exports.js";
|
||||
import {introductionSteps} from "./introduction.js";
|
||||
|
||||
export const tourIds = ["introduction", "interface-homepage"] as const;
|
||||
|
||||
export const tourIdsAndSteps: Array<
|
||||
[TourId, TourStepOptions[], TourStepEventHandler[]]
|
||||
> = [
|
||||
["introduction", introductionSteps, []],
|
||||
["interface-homepage", homepageSteps, homepageEventHandlers],
|
||||
];
|
|
@ -0,0 +1,4 @@
|
|||
export {
|
||||
eventHandlers as homepageEventHandlers,
|
||||
steps as homepageSteps,
|
||||
} from "./homepage.js";
|
|
@ -0,0 +1,499 @@
|
|||
import type Shepherd from "shepherd.js";
|
||||
import {
|
||||
addDatasetCounter,
|
||||
encapsulateElements,
|
||||
removeAllDatasetCounters,
|
||||
renderInContainer,
|
||||
} from "../utilities.js";
|
||||
|
||||
const step01 = renderInContainer(
|
||||
<>
|
||||
<h1>The Homepage</h1>
|
||||
|
||||
<p>
|
||||
If you plan on staying a while, this is likely the place you'll see the
|
||||
most. So let's work our way top to bottom, left to right.
|
||||
</p>
|
||||
|
||||
<p>Starting with...</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const step02 = renderInContainer(
|
||||
<>
|
||||
<h1>The Main Header</h1>
|
||||
|
||||
<p>
|
||||
On the left there is the Tildes logo and title, which you can click to get
|
||||
back to the homepage, or refresh it if you're already there. If you are in
|
||||
a group, the group name will also be shown.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
On the right is the notifications section and your username. When you have
|
||||
unread messages and/or are mentioned, they will show up next to your
|
||||
username. If you don't have any notifications right now, you can{" "}
|
||||
<a href="#" onClick={toggleMockNotifications}>
|
||||
click here
|
||||
</a>{" "}
|
||||
to show a preview of what they look like.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
At the moment mentions only work in comments, so if you get mentioned in a
|
||||
topic's text body and don't get a notification,{" "}
|
||||
<a target="_blank" href="https://gitlab.com/tildes/tildes/-/issues/195">
|
||||
that's why
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
// Toggle mock notifications, based on the code in the following link.
|
||||
// If the logic for this needs to be updated, also update the permalink to the
|
||||
// actual Tildes HTML code.
|
||||
// https://gitlab.com/tildes/tildes/-/blob/0dbb031562cd9297968fec0049af4b833ef77301/tildes/tildes/templates/macros/user.jinja2#L9-20
|
||||
function toggleMockNotifications(event: Event): void {
|
||||
// Prevent the click from going through so the anchor isn't removed.
|
||||
event.preventDefault();
|
||||
|
||||
const root = document.querySelector("#site-header .logged-in-user-info");
|
||||
function toggle(existingSelector: string, href: string, text: string): void {
|
||||
const existing =
|
||||
root?.querySelector<HTMLElement>(existingSelector) ?? undefined;
|
||||
if (existing === undefined) {
|
||||
// If no notifiaction exists, render our mock.
|
||||
const messageNotification = document.createElement("a");
|
||||
const count = 1 + Math.ceil(Math.random() * 10);
|
||||
messageNotification.classList.add("logged-in-user-alert");
|
||||
messageNotification.dataset.tildesShepherdMock = "true";
|
||||
messageNotification.href = href;
|
||||
messageNotification.textContent = `${count} ${text}`;
|
||||
root?.insertAdjacentElement("beforeend", messageNotification);
|
||||
} else if (existing.dataset.tildesShepherdMock === "true") {
|
||||
// If a notification exists and it has our mock attribute, remove it.
|
||||
existing.remove();
|
||||
} else {
|
||||
// A real message notification already exists, so we do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
toggle('[href="/messages/unread"]', "/messages/unread", "new messages");
|
||||
toggle(
|
||||
'[href="/notifications/unread"]',
|
||||
"/notifications/unread",
|
||||
"new comments",
|
||||
);
|
||||
}
|
||||
|
||||
const step03 = renderInContainer(
|
||||
<>
|
||||
<h1>The Listing Options</h1>
|
||||
|
||||
<p>
|
||||
Right below the main header are the topic listing options. These determine
|
||||
how the topics in the listing are sorted and{" "}
|
||||
<em>
|
||||
<b>from</b> what period
|
||||
</em>{" "}
|
||||
topics should be shown.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The non-activity sorts are the easiest to explain, so let's start with
|
||||
them:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<b>Votes</b> sorts topics with the most votes first.
|
||||
</li>
|
||||
<li>
|
||||
<b>Comments</b> sorts them with the most comments first.
|
||||
</li>
|
||||
<li>
|
||||
And <b>New</b> sorts them by their date, so newest topics first.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Then the first <b>Activity</b> will sort topics by bumping topics up as
|
||||
they receive comments, however this sort makes use of comment labels.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you don't know what comment labels are yet, don't worry, there is a
|
||||
tour that explains them. But in short they are a community tool to
|
||||
emphasize and de-emphasize comments, similar to votes but with more
|
||||
specific intention. If you'd like to read more, check out{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://docs.tildes.net/instructions/commenting-on-tildes#labelling-comments"
|
||||
>
|
||||
the documentation
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
And finally the <b>All activity</b> sorts topics the same as Activity, but
|
||||
without taking into account any comment labels.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you only want to see topics from a certain period, click on the "from"
|
||||
dropdown and select your period. Aside from a set of predefined options,
|
||||
you can also set a custom one by clicking the "other period" option, after
|
||||
which you'll be prompted for it.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const step04 = renderInContainer(
|
||||
<>
|
||||
<h1>The Topic Listing</h1>
|
||||
|
||||
<p>
|
||||
Next up, just below the listing options is the topic listing itself. We've
|
||||
only highlighted the first topic here, but you can probably see it
|
||||
continues all the way down the page.
|
||||
</p>
|
||||
|
||||
<p>Here, we've marked the main elements of the topic:</p>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
The topic title, when the topic is a "text topic" links to the same
|
||||
place as the comments link does. Otherwise when it's a "link topic", it
|
||||
will link to an external site.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The topic metadata, which by default includes the group it's in. If a
|
||||
topic has specific notable tags and you have the "show tags" setting
|
||||
enabled in your user settings, they will be shown after the group name.
|
||||
A "topic type" may also be shown to indicate whether the topic is a text
|
||||
topic, a video, an "ask" topic for recommendations or a survey, etc.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The amount of comments the topic has received. If any new comments have
|
||||
been posted since you last viewed the topic, in orange a "(X new)" will
|
||||
be added.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The topic source. For text topics this is always the poster of the
|
||||
topic, but for link topics in certain groups it will be the domain name
|
||||
together with that website's icon. For topics posted automatically by
|
||||
Tildes itself, it will be "Scheduled topic".
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>The list continues on the next page.</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const step05 = renderInContainer(
|
||||
<>
|
||||
<h1>The Topic Listing continued</h1>
|
||||
|
||||
<ol start={5}>
|
||||
<li>
|
||||
The date when the topic was posted. For dates that are pretty recent it
|
||||
will show something like "X days ago" while for longer dates it will be
|
||||
the exact date, like "April 26th, 2021".
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The voting button, clicking it will add your vote to the topic. On
|
||||
Tildes, once the topic is older than 30 days you can no longer vote on
|
||||
it and the individual voting data for is removed, with only the total
|
||||
count kept. You can read more about it in{" "}
|
||||
<a target="_blank" href="https://tild.es/jhm">
|
||||
this announcement topic
|
||||
</a>
|
||||
.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
And finally the topic actions. Clicking on the "Actions" button will
|
||||
show a dropdown including "Bookmark", "Ignore this topic" and if you
|
||||
have been granted the permission for it, "Edit title". Ignoring the
|
||||
topic will remove it from the topic listing and prevent any future
|
||||
notifications from happening. Both your bookmarked and ignored topics
|
||||
can be found in their respective sections on your profile.
|
||||
</li>
|
||||
</ol>
|
||||
</>,
|
||||
);
|
||||
|
||||
const step06 = renderInContainer(
|
||||
<>
|
||||
<h1>The Sidebar</h1>
|
||||
|
||||
<p>
|
||||
Let's take a look at the sidebar, where the first thing we're greeted with
|
||||
is the search bar.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A quick note for mobile use, the sidebar can opened via a button that
|
||||
appears in the very top-right of the page.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When searching from the homepage, topics will be included from any group.
|
||||
However, searching when inside a group will only include topics from that
|
||||
group and any sub-groups.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Below the search bar you will also find the title of the page, or name of
|
||||
the group, and a small description. The sidebar has more elements when in
|
||||
you're in a group page, but those are touched upon in the groups tour.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const step07 = renderInContainer(
|
||||
<>
|
||||
<h1>The Sidebar Subscriptions</h1>
|
||||
|
||||
<p>
|
||||
Moving on, you'll find the list of groups you are subscribed to and a
|
||||
button below it to go to the groups listing, where you can find all the
|
||||
that are available.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To post a topic, subscribe or unsubscribe from any group, go to the group
|
||||
in question and in the sidebar there will be buttons to do so.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const step08 = renderInContainer(
|
||||
<>
|
||||
<h1>The Sidebar User Settings</h1>
|
||||
|
||||
<p>
|
||||
At the bottom of the sidebar you will find some user settings.
|
||||
Specifically the filtered topics tags you have set and a link to the
|
||||
settings page.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When you have filtered topics tags set, any topics with any of the tags
|
||||
present will be removed from your listing. You can read more about it by
|
||||
going to the dedicated page for it by clicking the "Edit filtered tags"
|
||||
button.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const step09 = renderInContainer(
|
||||
<>
|
||||
<h1>The Footer</h1>
|
||||
|
||||
<p>
|
||||
And finally, the very last part of the homepage is the footer. Here you
|
||||
will find a theme selector (more on that in a moment) and various links to
|
||||
places, such as the Tildes Documentation, Contact page, the source code,
|
||||
etc. We highly recommend reading through the documentation as it explains
|
||||
a lot of the how and why Tildes does certain things.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
As for the theme selector, you can change your theme with a number of
|
||||
options. When you change it here, it will only change for the current
|
||||
device, if you'd like to set an account default for all devices, head to
|
||||
your account settings.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const step10 = renderInContainer(
|
||||
<>
|
||||
<h1>This is the end, beautiful friend</h1>
|
||||
|
||||
<p>And that's the end of the Homepage tour!</p>
|
||||
|
||||
<p>
|
||||
As always, if you find any bugs, have feature requests or simply want to
|
||||
ask a question. Please send us a message at{" "}
|
||||
<a target="_blank" href="https://tildes.net/user/Community">
|
||||
@Community
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
|
||||
<p>Happy Tildying!~</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
export const steps: Shepherd.Step.StepOptions[] = [
|
||||
{
|
||||
id: "homepage-01",
|
||||
text: step01,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: "#site-header",
|
||||
on: "bottom",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "homepage-02",
|
||||
scrollTo: true,
|
||||
text: step02,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: "main > .listing-options",
|
||||
on: "bottom",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "homepage-03",
|
||||
text: step03,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: ".topic-listing > li:first-child",
|
||||
on: "bottom",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "homepage-04",
|
||||
text: step04,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: ".topic-listing > li:first-child",
|
||||
on: "bottom",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "homepage-05",
|
||||
text: step05,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: '#sidebar [data-tildes-shepherd-encapsulated="homepage-06"]',
|
||||
on: "left-start",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "homepage-06",
|
||||
text: step06,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: '#sidebar [data-tildes-shepherd-encapsulated="homepage-07"]',
|
||||
on: "left-start",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "homepage-07",
|
||||
scrollTo: true,
|
||||
text: step07,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: "#sidebar .divider + .nav",
|
||||
on: "left-start",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "homepage-08",
|
||||
scrollTo: true,
|
||||
text: step08,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: "#site-footer",
|
||||
on: "top",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "homepage-09",
|
||||
scrollTo: true,
|
||||
text: step09,
|
||||
},
|
||||
{
|
||||
canClickTarget: false,
|
||||
id: "homepage-10",
|
||||
text: step10,
|
||||
},
|
||||
];
|
||||
|
||||
export const eventHandlers: TourStepEventHandler[] = [
|
||||
[
|
||||
"homepage-04",
|
||||
[
|
||||
"show",
|
||||
() => {
|
||||
const topic = ".topic-listing > li:first-child";
|
||||
const counters = [
|
||||
".topic-title",
|
||||
".topic-metadata",
|
||||
".topic-info-comments",
|
||||
".topic-info-source",
|
||||
"time",
|
||||
".topic-voting",
|
||||
".topic-actions",
|
||||
];
|
||||
|
||||
for (const [count, selector] of counters.entries()) {
|
||||
addDatasetCounter(`${topic} ${selector}`, count + 1);
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
"homepage-05",
|
||||
[
|
||||
"destroy",
|
||||
() => {
|
||||
removeAllDatasetCounters();
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
"homepage-05",
|
||||
[
|
||||
"show",
|
||||
() => {
|
||||
encapsulateElements(
|
||||
"homepage-06",
|
||||
"#sidebar .sidebar-controls",
|
||||
"afterend",
|
||||
["#sidebar .form-search", "#sidebar h2", "#sidebar p"],
|
||||
);
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
"homepage-06",
|
||||
[
|
||||
"show",
|
||||
() => {
|
||||
encapsulateElements("homepage-07", "#sidebar .divider", "beforebegin", [
|
||||
"#sidebar .nav",
|
||||
'#sidebar [href="/groups"',
|
||||
]);
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
"homepage-08",
|
||||
[
|
||||
"show",
|
||||
() => {
|
||||
const filteredTags =
|
||||
document.querySelector<HTMLDetailsElement>("#sidebar details") ??
|
||||
undefined;
|
||||
if (filteredTags === undefined) {
|
||||
console.warn("Element is unexpectedly undefined");
|
||||
return;
|
||||
}
|
||||
|
||||
filteredTags.open = true;
|
||||
},
|
||||
],
|
||||
],
|
||||
];
|
|
@ -0,0 +1,122 @@
|
|||
import type Shepherd from "shepherd.js";
|
||||
import {createIntroductionUnderstood} from "../storage/common.js";
|
||||
import {openOptionsPageFromBackground, renderInContainer} from "./utilities.js";
|
||||
|
||||
const stepOne = renderInContainer(
|
||||
<>
|
||||
<h1>Thank you for installing Tildes Shepherd!</h1>
|
||||
|
||||
<p>
|
||||
Tildes Shepherd is a set of interactive guided tours for Tildes to show
|
||||
you around and introduce you to the concepts that make this website great.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To see and start the tours that are available, click on the extension icon
|
||||
to go to the options page or{" "}
|
||||
<a href="#" onClick={openOptionsPageFromBackground}>
|
||||
click here now
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Each tour will start with a pop-up like this one and have "Continue",
|
||||
"Back" and "Exit" buttons at the bottom. They will progress the tour
|
||||
forward, backward or exit it.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const stepTwo = renderInContainer(
|
||||
<>
|
||||
<h1>Tour Mechanics</h1>
|
||||
|
||||
<p>
|
||||
During the tours, at various points we will want to highlight specific
|
||||
areas. When that happens you won't be able to click anything inside them,
|
||||
mainly to prevent you from accidentally going to a different page and
|
||||
interrupting the tour. But also to prevent you from taking actions you
|
||||
maybe don't want to take, like voting on something you don't necessarily
|
||||
want to vote on.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Depending on your selected theme, the highlighted areas will have a yellow
|
||||
or orange border and some extra added whitespace. As you can see in this
|
||||
example where the main site header has been highlighted.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
const stepThree = renderInContainer(
|
||||
<>
|
||||
<p>
|
||||
If you find any bugs, have feature requests or simply want to ask a
|
||||
question. Please send us a message at{" "}
|
||||
<a target="_blank" href="https://tildes.net/user/Community">
|
||||
@Community
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Also, big shoutout to the people at{" "}
|
||||
<a target="_blank" href="https://shipshape.io">
|
||||
Ship Shape
|
||||
</a>{" "}
|
||||
for making{" "}
|
||||
<a target="_blank" href="https://shepherdjs.dev">
|
||||
Shepherd.js
|
||||
</a>
|
||||
, the software library making this entire project so much easier to
|
||||
create. And the namesake of it, too.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Once you click the "I understand" button below, this message won't pop up
|
||||
again, so remember the extension icon is how you get to the tours.
|
||||
</p>
|
||||
|
||||
<p>Happy Tildying!~</p>
|
||||
</>,
|
||||
);
|
||||
|
||||
export const introductionSteps: Shepherd.Step.StepOptions[] = [
|
||||
{
|
||||
id: "introduction-1",
|
||||
text: stepOne,
|
||||
},
|
||||
{
|
||||
attachTo: {
|
||||
element: "#site-header",
|
||||
on: "bottom",
|
||||
},
|
||||
canClickTarget: false,
|
||||
id: "introduction-2",
|
||||
text: stepTwo,
|
||||
},
|
||||
{
|
||||
buttons: [
|
||||
{
|
||||
classes: "btn",
|
||||
text: "I understand",
|
||||
async action() {
|
||||
const introductionUnderstood = await createIntroductionUnderstood();
|
||||
introductionUnderstood.value = true;
|
||||
await introductionUnderstood.save();
|
||||
this.complete();
|
||||
},
|
||||
},
|
||||
{
|
||||
classes: "btn",
|
||||
text: "Back",
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
id: "introduction-3",
|
||||
text: stepThree,
|
||||
},
|
||||
];
|
|
@ -0,0 +1,106 @@
|
|||
import {type JSX, render} from "preact";
|
||||
import browser from "webextension-polyfill";
|
||||
|
||||
/**
|
||||
* Adds a `[data-tildes-shepherd-counter]` attribute to a specified element. For
|
||||
* the associated CSS, see `source/content-scripts/scss/main.scss`.
|
||||
*
|
||||
* @param selector The selector of element to apply the counter to, if the
|
||||
* target element can't be selected an error will be thrown.
|
||||
* @param count The number to display in the counter.
|
||||
*/
|
||||
export function addDatasetCounter(selector: string, count: number) {
|
||||
const element = document.querySelector<HTMLElement>(selector) ?? undefined;
|
||||
if (element === undefined) {
|
||||
throw new Error(`Target element for "${selector}" is undefined`);
|
||||
}
|
||||
|
||||
element.dataset.tildesShepherdCounter = count.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new `div` element and put all the specified elements inside it. Used
|
||||
* for highlighting groups of elements that have sibling elements we may not
|
||||
* want to highlight. Like highlighting only the search bar, title and
|
||||
* description in the homepage sidebar, but not all the rest of the sidebar.
|
||||
* @param stepId The unique step ID to set as
|
||||
* `data-tildes-shepherd-encapsulated="<id>"`. Used for attaching the Shepherd
|
||||
* step to the container.
|
||||
* @param rootSelector The selector for the root element to insert the container
|
||||
* near. This doesn't need to be the parent element necessarily, since
|
||||
* {@link Element.insertAdjacentElement} is used to do the inserting it can also
|
||||
* be inserted before or after the root element.
|
||||
* @param position The insert position for {@link Element.insertAdjacentElement}.
|
||||
* @param selectors The list of element selectors to include in the new
|
||||
* container. Note that you should make sure any elements included don't break
|
||||
* their CSS when contained by a new `div` element and ideally they should all
|
||||
* be adjacent to one another and specified in the order they appear in.
|
||||
*/
|
||||
export function encapsulateElements(
|
||||
stepId: string,
|
||||
rootSelector: string,
|
||||
position: InsertPosition,
|
||||
selectors: string[],
|
||||
) {
|
||||
const container = document.createElement("div");
|
||||
container.dataset.tildesShepherdEncapsulated = stepId;
|
||||
|
||||
for (const selector of selectors) {
|
||||
const element = document.querySelector<HTMLElement>(selector) ?? undefined;
|
||||
if (element === undefined) {
|
||||
console.warn(`Unexpected undefined element: ${selector}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't encapsulate anything if the parent is already one of our elements.
|
||||
if (
|
||||
element.parentElement?.dataset.tildesShepherdEncapsulated !== undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
container.append(element);
|
||||
}
|
||||
|
||||
const root = document.querySelector(rootSelector) ?? undefined;
|
||||
if (root === undefined) {
|
||||
throw new Error(`Root selector returned undefined: ${rootSelector}`);
|
||||
}
|
||||
|
||||
root.insertAdjacentElement(position, container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content scripts can't open the options page themselves, so send a message to
|
||||
* the background script and open it there.
|
||||
* @param event The mouse click event to prevent from happening.
|
||||
*/
|
||||
export async function openOptionsPageFromBackground(
|
||||
event: MouseEvent,
|
||||
): Promise<void> {
|
||||
event.preventDefault();
|
||||
await browser.runtime.sendMessage("open-options-page");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements with a `data-tildes-shepherd-counter` set.
|
||||
*/
|
||||
export function removeAllDatasetCounters() {
|
||||
const elements = document.querySelectorAll<HTMLElement>(
|
||||
"[data-tildes-shepherd-counter]",
|
||||
);
|
||||
for (const element of Array.from(elements)) {
|
||||
delete element.dataset.tildesShepherdCounter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render JSX inside a `div` and return the div. This is mostly used so we can
|
||||
* pass JSX more easily to Shepherd, which doesn't accept JSX directly but does
|
||||
* accept {@link HTMLElement}. So this bit of indirection gets us there.
|
||||
*/
|
||||
export function renderInContainer(root: JSX.Element): HTMLElement {
|
||||
const container = document.createElement("div");
|
||||
render(root, container);
|
||||
return container;
|
||||
}
|
|
@ -1,8 +1,14 @@
|
|||
// Export something so TypeScript doesn't see this file as an ambient module.
|
||||
export {};
|
||||
import type Shepherd from "shepherd.js";
|
||||
import type {tourIds} from "./tours/exports.js";
|
||||
|
||||
declare global {
|
||||
const $browser: "chromium" | "firefox";
|
||||
const $dev: boolean;
|
||||
const $test: boolean;
|
||||
|
||||
type TourId = (typeof tourIds)[number];
|
||||
type TourStepEvent = "show" | "destroy";
|
||||
type TourStepEventFunction = Parameters<Shepherd.Step["on"]>[1];
|
||||
type TourStepEventHandler = [string, [TourStepEvent, TourStepEventFunction]];
|
||||
type TourStepOptions = Shepherd.Step.StepOptions;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue