1
Fork 0

Add a basic options page.

This commit is contained in:
Bauke 2023-06-15 15:06:41 +02:00
parent 4d600e200d
commit e0c4e8828e
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
9 changed files with 303 additions and 0 deletions

14
source/options/app.tsx Normal file
View File

@ -0,0 +1,14 @@
import {Component} from "preact";
import {PageHeader} from "./components/page-header.js";
import {Tours} from "./components/tours.js";
export class App extends Component {
render() {
return (
<>
<PageHeader />
<Tours />
</>
);
}
}

View File

@ -0,0 +1,12 @@
import {Component} from "preact";
export class PageHeader extends Component {
render() {
return (
<header class="page-header">
<img src="/tildes-shepherd.png" alt="The Tildes Shepherd logo." />
<h1>Tildes Shepherd</h1>
</header>
);
}
}

View File

@ -0,0 +1,74 @@
import {Component, type JSX} from "preact";
type Props = {
hasBeenCompleted: boolean;
name: string;
tourId: TourId;
};
function tourDescription(tourId: Props["tourId"]): JSX.Element {
if (tourId === "introduction") {
return (
<p class="tour-description">
A short introduction to Tildes Shepherd and how the tours work. Normally
this is automatically shown when you first installed the extension.
</p>
);
}
if (tourId === "interface-homepage") {
return (
<p class="tour-description">
Let's take a look at the home page and all we can do there.
</p>
);
}
return (
<p class="tour-description">
Tour ID "{tourId}" does not have a description, this should probably be
fixed!
</p>
);
}
function tourLink(tourId: Props["tourId"]): string {
const anchor = `#tildes-shepherd-tour=${tourId}`;
const baseUrl = "https://tildes.net";
let path = "";
switch (tourId) {
case "interface-homepage":
case "introduction": {
path = "/";
break;
}
default:
}
return `${baseUrl}${path}${anchor}`;
}
export class Tour extends Component<Props> {
render() {
const {hasBeenCompleted, name, tourId} = this.props;
const classes = ["tour", hasBeenCompleted ? "completed" : ""].join(" ");
const completed = hasBeenCompleted ? (
<p class="tour-completed" title="You've completed this tour before!">
</p>
) : undefined;
return (
<div class={classes.trim()}>
<h3>{name}</h3>
{completed}
{tourDescription(tourId)}
<p class="tour-link">
<a href={tourLink(tourId)}>Take this tour</a>
</p>
</div>
);
}
}

View File

@ -0,0 +1,52 @@
import {Component, type JSX} from "preact";
import {createToursCompleted} from "../../storage/common.js";
import {Tour} from "./tour.js";
type Props = Record<string, unknown>;
type State = {
toursCompleted: Awaited<ReturnType<typeof createToursCompleted>>["value"];
};
export class Tours extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
toursCompleted: new Set(),
};
}
async componentDidMount(): Promise<void> {
const toursCompleted = await createToursCompleted();
this.setState({toursCompleted: toursCompleted.value});
}
render(): JSX.Element {
const {toursCompleted} = this.state;
const createTour = (tourId: TourId, name: string): Tour["props"] => {
return {
hasBeenCompleted: toursCompleted.has(tourId),
name,
tourId,
};
};
const tourProps: Array<Tour["props"]> = [
createTour("introduction", "Introduction"),
createTour("interface-homepage", "The Homepage"),
];
return (
<main>
<h2>Tours</h2>
<div class="tours">
{tourProps.map((props) => (
<Tour {...props} />
))}
</div>
</main>
);
}
}

8
source/options/setup.tsx Normal file
View File

@ -0,0 +1,8 @@
import {render} from "preact";
import "modern-normalize/modern-normalize.css";
import "../scss/global.scss";
import {App} from "./app.js";
const preactRoot = document.createElement("div");
document.body.append(preactRoot);
render(<App />, preactRoot);

View File

@ -0,0 +1,11 @@
.page-header {
display: flex;
margin-top: 16px;
padding: 0 16px;
img {
aspect-ratio: 1;
height: 5rem;
margin-right: 1rem;
}
}

View File

@ -0,0 +1,61 @@
@use "sass:list";
.tours {
display: grid;
gap: 16px;
grid-template-columns: repeat(3, 1fr);
margin-top: 16px;
}
.tour {
background-color: var(--background-secondary);
border: 2px solid var(--tour-accent-color);
display: grid;
gap: 8px;
grid-template-columns: auto;
grid-template-rows: min-content auto min-content;
padding: 16px;
&.completed {
grid-template-columns: auto min-content;
.tour-description,
.tour-link {
grid-column: 1 / 3;
}
}
$border-colors: (
"red",
"orange",
"yellow",
"green",
"cyan",
"blue",
"violet",
"magenta",
);
@each $color in $border-colors {
$color-number: list.index($border-colors, $color);
&:nth-child(#{list.length($border-colors)}n + #{$color-number}) {
--tour-accent-color: var(--#{$color});
--tour-light-accent-color: var(--light-#{$color});
}
}
.tour-link a {
color: var(--tour-light-accent-color);
font-weight: bold;
text-decoration: underline;
&:hover {
color: var(--foreground);
}
}
.tour-completed {
color: var(--light-green);
}
}

56
source/scss/global.scss Normal file
View File

@ -0,0 +1,56 @@
@use "reset";
@use "components/page-header";
@use "components/tours";
html {
font-size: 62.5%;
}
body {
--background-primary: #00171d;
--background-secondary: #002b36;
--background-tertiary: #000;
--foreground: #fdf6e3;
--red: #dc322f;
--light-red: #e35d5b;
--dark-red: #b9221f;
--orange: #cb4b16;
--light-orange: #e8632c;
--dark-orange: #9d3a11;
--yellow: #b58900;
--light-yellow: #e8b000;
--dark-yellow: #826200;
--green: #859900;
--light-green: #b1cc00;
--dark-green: #596600;
--cyan: #2aa198;
--light-cyan: #35c9be;
--dark-cyan: #1f7972;
--blue: #268bd2;
--light-blue: #4ca2df;
--dark-blue: #1e6ea7;
--violet: #6c71c4;
--light-violet: #9094d3;
--dark-violet: #484fb5;
--magenta: #d33682;
--light-magenta: #dc609c;
--dark-magenta: #b02669;
background-color: var(--background-primary);
color: var(--foreground);
font-size: 2rem;
}
a {
color: var(--accent-1);
text-decoration: none;
&:hover {
color: var(--accent-2);
text-decoration: underline;
}
}
main {
padding: 16px;
}

15
source/scss/reset.scss Normal file
View File

@ -0,0 +1,15 @@
blockquote,
code,
h1,
h2,
h3,
h4,
h5,
li,
ol,
p,
pre,
ul {
margin: 0;
padding: 0;
}