Add a basic options page.
This commit is contained in:
parent
4d600e200d
commit
e0c4e8828e
|
@ -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 />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
@ -0,0 +1,11 @@
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
height: 5rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
blockquote,
|
||||||
|
code,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
li,
|
||||||
|
ol,
|
||||||
|
p,
|
||||||
|
pre,
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
Loading…
Reference in New Issue