Frontend responsive UI tweaks (#20)
This commit is contained in:
parent
14f2d13a73
commit
5722909157
@ -2,12 +2,15 @@
|
||||
<html data-theme="light">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Demo</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
||||
/>
|
||||
<title>Browsertrix Cloud</title>
|
||||
<base href="/" />
|
||||
<script src="/main.js"></script>
|
||||
</head>
|
||||
<body class="min-w-screen min-h-screen">
|
||||
<browsertrix-app
|
||||
class="flex flex-col min-h-screen bg-blue-400"
|
||||
></browsertrix-app>
|
||||
<body>
|
||||
<browsertrix-app></browsertrix-app>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
"@lit/localize": "^0.11.1",
|
||||
"@shoelace-style/shoelace": "^2.0.0-beta.61",
|
||||
"axios": "^0.22.0",
|
||||
"color": "^4.0.1",
|
||||
"lit": "^2.0.0",
|
||||
"lit-element-router": "^2.0.3",
|
||||
"path-parser": "^6.1.0",
|
||||
@ -30,6 +31,7 @@
|
||||
"devDependencies": {
|
||||
"@esm-bundle/chai": "^4.3.4-fix.0",
|
||||
"@lit/localize-tools": "^0.5.0",
|
||||
"@types/color": "^3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@web/dev-server-esbuild": "^0.2.16",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { msg, updateWhenLocaleChanges } from "@lit/localize";
|
||||
|
||||
import "./shoelace";
|
||||
@ -85,73 +86,109 @@ export class App extends LiteElement {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.renderNavBar()}
|
||||
<div class="w-full h-full px-12 py-12">${this.renderPage()}</div>
|
||||
<footer class="flex justify-center p-4">
|
||||
<locale-picker></locale-picker>
|
||||
</footer>
|
||||
<style>
|
||||
${theme}
|
||||
</style>
|
||||
|
||||
<div class="min-w-screen min-h-screen flex flex-col">
|
||||
${this.renderNavBar()}
|
||||
<main class="relative flex-auto flex">${this.renderPage()}</main>
|
||||
<footer class="flex justify-center p-4 border-t">
|
||||
<locale-picker></locale-picker>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
renderNavBar() {
|
||||
return html`
|
||||
<style>
|
||||
${theme}
|
||||
</style>
|
||||
|
||||
<div
|
||||
class="flex p-2 items-center shadow-lg bg-white text-neutral-content"
|
||||
<nav
|
||||
class="flex items-center justify-between p-2 bg-gray-900 text-gray-50"
|
||||
>
|
||||
<div class="flex-1 px-2 mx-2">
|
||||
<a href="/" class="text-lg font-bold" @click="${this.navLink}"
|
||||
>${msg("Browsertrix Cloud")}</a
|
||||
<div>
|
||||
<a href="/" @click="${this.navLink}"
|
||||
><h1 class="text-base px-2">${msg("Browsertrix Cloud")}</h1></a
|
||||
>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
<div>
|
||||
${this.authState
|
||||
? html` <a
|
||||
class="font-bold px-4"
|
||||
href="/my-account"
|
||||
@click="${this.navLink}"
|
||||
>${msg("My Account")}</a
|
||||
>
|
||||
<button class="btn btn-error" @click="${this.onLogOut}">
|
||||
${msg("Log Out")}
|
||||
</button>`
|
||||
: html`
|
||||
<sl-button type="primary" @click="${this.onNeedLogin}">
|
||||
${msg("Log In")}
|
||||
</sl-button>
|
||||
`}
|
||||
? html` <sl-dropdown>
|
||||
<div class="p-2" role="button" slot="trigger">
|
||||
${this.authState.username}
|
||||
<span class="text-xs"
|
||||
><sl-icon name="chevron-down"></sl-icon
|
||||
></span>
|
||||
</div>
|
||||
<sl-menu>
|
||||
<sl-menu-item>Your account</sl-menu-item>
|
||||
<sl-menu-item @click="${this.onLogOut}"
|
||||
>${msg("Log Out")}</sl-menu-item
|
||||
>
|
||||
</sl-menu>
|
||||
</sl-dropdown>`
|
||||
: html` <a href="/log-in"> ${msg("Log In")} </a> `}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
`;
|
||||
}
|
||||
|
||||
renderPage() {
|
||||
const navLink = ({ href, label }: { href: string; label: string }) => html`
|
||||
<li>
|
||||
<a
|
||||
class="block p-2 ${href === this.viewState._path
|
||||
? "text-primary"
|
||||
: ""}"
|
||||
href="${href}"
|
||||
@click="${this.navLink}"
|
||||
>${label}</a
|
||||
>
|
||||
</li>
|
||||
`;
|
||||
const appLayout = (template: TemplateResult) => html`
|
||||
<div class="w-full flex flex-col md:flex-row">
|
||||
<nav class="md:w-80 md:p-4 md:border-r">
|
||||
<ul class="flex md:flex-col">
|
||||
${navLink({ href: "/my-account", label: "Archives" })}
|
||||
${navLink({ href: "/users", label: "Users" })}
|
||||
</ul>
|
||||
</nav>
|
||||
${template}
|
||||
</div>
|
||||
`;
|
||||
|
||||
switch (this.viewState._route) {
|
||||
case "login":
|
||||
return html`<log-in @logged-in="${this.onLoggedIn}"></log-in>`;
|
||||
return html`<log-in
|
||||
class="w-full md:bg-gray-100 flex items-center justify-center"
|
||||
@logged-in="${this.onLoggedIn}"
|
||||
></log-in>`;
|
||||
|
||||
case "home":
|
||||
return html`<div>Home</div>`;
|
||||
return html`<div class="w-full flex items-center justify-center">
|
||||
<sl-button type="primary" size="large" @click="${this.onNeedLogin}">
|
||||
${msg("Log In")}
|
||||
</sl-button>
|
||||
</div>`;
|
||||
|
||||
case "my-account":
|
||||
return html`<my-account
|
||||
return appLayout(html`<my-account
|
||||
class="w-full"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
@need-login="${this.onNeedLogin}"
|
||||
.authState="${this.authState}"
|
||||
></my-account>`;
|
||||
></my-account>`);
|
||||
|
||||
case "archive-info":
|
||||
case "archive-info-tab":
|
||||
return html`<btrix-archive
|
||||
return appLayout(html`<btrix-archive
|
||||
class="w-full"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
.authState="${this.authState}"
|
||||
.viewState="${this.viewState}"
|
||||
aid="${this.viewState.aid!}"
|
||||
tab="${this.viewState.tab || "running"}"
|
||||
></btrix-archive>`;
|
||||
></btrix-archive>`);
|
||||
|
||||
default:
|
||||
return html`<div>Not Found!</div>`;
|
||||
|
||||
@ -1,52 +1,61 @@
|
||||
import { state, property } from "lit/decorators.js";
|
||||
import LiteElement, { html } from "../utils/LiteElement";
|
||||
import type { Auth } from "../types/auth";
|
||||
|
||||
export class LogInPage extends LiteElement {
|
||||
@property({ type: Object })
|
||||
auth?: Auth;
|
||||
loginError: string = "";
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
loginError: { type: String },
|
||||
};
|
||||
}
|
||||
@state()
|
||||
isLoggingIn: boolean = false;
|
||||
|
||||
@state()
|
||||
loginError?: string;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="flex items-center justify-center min-h-screen bg-blue-400">
|
||||
<div class="bg-white shadow-2xl rounded-xl px-12 py-12">
|
||||
<div class="max-w-md">
|
||||
<sl-form @sl-submit="${this.onSubmit}">
|
||||
<div class="mb-5">
|
||||
<sl-input
|
||||
name="username"
|
||||
label="Username"
|
||||
placeholder="Username"
|
||||
required
|
||||
>
|
||||
</sl-input>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<sl-input
|
||||
name="password"
|
||||
type="password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
required
|
||||
>
|
||||
</sl-input>
|
||||
</div>
|
||||
<sl-button class="w-full" type="primary" submit>Log in</sl-button>
|
||||
</sl-form>
|
||||
<div class="md:bg-white md:shadow-2xl md:rounded-lg md:px-12 md:py-12">
|
||||
<div class="max-w-md">
|
||||
<sl-form @sl-submit="${this.onSubmit}">
|
||||
<div class="mb-5">
|
||||
<sl-input
|
||||
id="username"
|
||||
name="username"
|
||||
label="Username"
|
||||
placeholder="Username"
|
||||
required
|
||||
>
|
||||
</sl-input>
|
||||
</div>
|
||||
<div class="mb-5">
|
||||
<sl-input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
required
|
||||
>
|
||||
</sl-input>
|
||||
</div>
|
||||
<sl-button
|
||||
class="w-full"
|
||||
type="primary"
|
||||
?loading=${this.isLoggingIn}
|
||||
submit
|
||||
>Log in</sl-button
|
||||
>
|
||||
</sl-form>
|
||||
|
||||
<div id="login-error" class="text-red-600">${this.loginError}</div>
|
||||
</div>
|
||||
<div id="login-error" class="text-red-600">${this.loginError}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async onSubmit(event: { detail: { formData: FormData } }) {
|
||||
this.isLoggingIn = true;
|
||||
|
||||
const { formData } = event.detail;
|
||||
|
||||
const username = formData.get("username") as string;
|
||||
@ -65,6 +74,7 @@ export class LogInPage extends LiteElement {
|
||||
body: params.toString(),
|
||||
});
|
||||
if (resp.status !== 200) {
|
||||
this.isLoggingIn = false;
|
||||
this.loginError = "Sorry, invalid credentials";
|
||||
return;
|
||||
}
|
||||
@ -83,5 +93,7 @@ export class LogInPage extends LiteElement {
|
||||
if (!this.auth) {
|
||||
this.loginError = "Unknown login response";
|
||||
}
|
||||
|
||||
this.isLoggingIn = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,14 @@
|
||||
* Cherry-picked Shoelace components
|
||||
* https://shoelace.style
|
||||
*/
|
||||
import { setBasePath } from "@shoelace-style/shoelace/dist/utilities/base-path.js";
|
||||
import "@shoelace-style/shoelace/dist/themes/light.css";
|
||||
import "@shoelace-style/shoelace/dist/components/button/button";
|
||||
import "@shoelace-style/shoelace/dist/components/form/form";
|
||||
import "@shoelace-style/shoelace/dist/components/icon/icon";
|
||||
import "@shoelace-style/shoelace/dist/components/input/input";
|
||||
import "@shoelace-style/shoelace/dist/components/menu/menu";
|
||||
import "@shoelace-style/shoelace/dist/components/menu-item/menu-item";
|
||||
import "@shoelace-style/shoelace/dist/components/select/select";
|
||||
|
||||
setBasePath("/shoelace");
|
||||
|
||||
@ -1,11 +1,50 @@
|
||||
/**
|
||||
* Shoelace CSS theming variables
|
||||
* https://github.com/shoelace-style/shoelace/blob/current/src/themes/light.styles.ts
|
||||
*
|
||||
* To make new variables available to Tailwind, update
|
||||
* `theme` in tailwind.cofnig.js
|
||||
*/
|
||||
import { css } from "lit";
|
||||
import { css, unsafeCSS } from "lit";
|
||||
import Color from "color";
|
||||
|
||||
// TODO generate at build time
|
||||
const PRIMARY_COLOR = "#4876ff";
|
||||
const primaryColor = Color(PRIMARY_COLOR);
|
||||
|
||||
const theme = css`
|
||||
:root {
|
||||
/* TODO add custom variables here */
|
||||
--primary: ${unsafeCSS(PRIMARY_COLOR)};
|
||||
|
||||
/*
|
||||
* Theme Tokens
|
||||
*/
|
||||
/* Primary */
|
||||
--sl-color-primary-50: ${unsafeCSS(primaryColor.lighten(0.6))};
|
||||
--sl-color-primary-100: ${unsafeCSS(primaryColor.lighten(0.5))};
|
||||
--sl-color-primary-200: ${unsafeCSS(primaryColor.lighten(0.4))};
|
||||
--sl-color-primary-300: ${unsafeCSS(primaryColor.lighten(0.3))};
|
||||
--sl-color-primary-400: ${unsafeCSS(primaryColor.lighten(0.2))};
|
||||
--sl-color-primary-500: ${unsafeCSS(primaryColor.lighten(0.1))};
|
||||
--sl-color-primary-600: var(--primary);
|
||||
--sl-color-primary-700: ${unsafeCSS(primaryColor.darken(0.1))};
|
||||
--sl-color-primary-800: ${unsafeCSS(primaryColor.darken(0.2))};
|
||||
--sl-color-primary-900: ${unsafeCSS(primaryColor.darken(0.3))};
|
||||
--sl-color-primary-950: ${unsafeCSS(primaryColor.darken(0.4))};
|
||||
|
||||
/*
|
||||
* Forms
|
||||
*/
|
||||
|
||||
/* Buttons */
|
||||
--sl-button-font-size-small: var(--sl-font-size-small);
|
||||
--sl-button-font-size-medium: var(--sl-font-size-medium);
|
||||
--sl-button-font-size-large: var(--sl-font-size-large);
|
||||
|
||||
/* Labels */
|
||||
--sl-input-label-font-size-small: var(--sl-font-size-x-small);
|
||||
--sl-input-label-font-size-medium: var(--sl-font-size-small);
|
||||
--sl-input-label-font-size-large: var(--sl-font-size-medium);
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@ -21,7 +21,10 @@ function makeTheme() {
|
||||
}));
|
||||
|
||||
return {
|
||||
colors: colors.map(makeColorPalette),
|
||||
colors: {
|
||||
...colors.map(makeColorPalette),
|
||||
primary: `var(--primary)`,
|
||||
},
|
||||
fontFamily: {
|
||||
sans: `var(--sl-font-sans)`,
|
||||
serif: `var(--sl-font-serif)`,
|
||||
|
||||
@ -10,6 +10,11 @@ require("dotenv").config({
|
||||
});
|
||||
|
||||
const backendUrl = new URL(process.env.API_BASE_URL || "http://btrix.cloud/");
|
||||
const shoelaceAssetsSrcPath = path.resolve(
|
||||
__dirname,
|
||||
"node_modules/@shoelace-style/shoelace/dist/assets"
|
||||
);
|
||||
const shoelaceAssetsPublicPath = "/shoelace/assets";
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/index.ts",
|
||||
@ -49,11 +54,18 @@ module.exports = {
|
||||
open: true,
|
||||
compress: true,
|
||||
hot: true,
|
||||
static: {
|
||||
directory: path.join(__dirname),
|
||||
//publicPath: "/",
|
||||
watch: true,
|
||||
},
|
||||
static: [
|
||||
{
|
||||
directory: shoelaceAssetsSrcPath,
|
||||
publicPath: shoelaceAssetsPublicPath,
|
||||
},
|
||||
|
||||
{
|
||||
directory: path.join(__dirname),
|
||||
//publicPath: "/",
|
||||
watch: true,
|
||||
},
|
||||
],
|
||||
historyApiFallback: true,
|
||||
proxy: {
|
||||
"/api": {
|
||||
@ -82,11 +94,8 @@ module.exports = {
|
||||
patterns: [
|
||||
// Copy Shoelace assets to dist/shoelace
|
||||
{
|
||||
from: path.resolve(
|
||||
__dirname,
|
||||
"node_modules/@shoelace-style/shoelace/dist/assets"
|
||||
),
|
||||
to: path.resolve(__dirname, "dist/shoelace/assets"),
|
||||
from: shoelaceAssetsSrcPath,
|
||||
to: path.resolve(__dirname, "dist", shoelaceAssetsPublicPath),
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
||||
@ -252,6 +252,25 @@
|
||||
"@types/node" "*"
|
||||
"@types/qs" "*"
|
||||
|
||||
"@types/color-convert@*":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22"
|
||||
integrity sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==
|
||||
dependencies:
|
||||
"@types/color-name" "*"
|
||||
|
||||
"@types/color-name@*":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||
|
||||
"@types/color@^3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/color/-/color-3.0.2.tgz#3779043e782f562aa9157b5fc6bd07e14fd8e7f3"
|
||||
integrity sha512-INiJl6sfNn8iyC5paxVzqiVUEj2boIlFki02uRTAkKwAj++7aAF+ZfEv/XrIeBa0XI/fTZuDHW8rEEcEVnON+Q==
|
||||
dependencies:
|
||||
"@types/color-convert" "*"
|
||||
|
||||
"@types/command-line-args@^5.0.0":
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/command-line-args/-/command-line-args-5.2.0.tgz#adbb77980a1cc376bb208e3f4142e907410430f6"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user