ui work
This commit is contained in:
parent
ab366664f5
commit
4311b82976
|
@ -0,0 +1,13 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
|
||||
plugins: ['svelte3', '@typescript-eslint'],
|
||||
ignorePatterns: ['*.cjs'],
|
||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
||||
settings: {
|
||||
'svelte3/typescript': () => require('typescript')
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
}
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
|
@ -0,0 +1,5 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/tfweb.iml" filepath="$PROJECT_DIR$/.idea/tfweb.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1 @@
|
|||
engine-strict=true
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "tfweb",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "eslint .",
|
||||
"typesafe-i18n": "typesafe-i18n"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^2.0.0",
|
||||
"@sveltejs/kit": "^1.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"postcss": "^8.4.21",
|
||||
"svelte": "^3.54.0",
|
||||
"svelte-check": "^3.0.1",
|
||||
"tailwindcss": "^3.2.7",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.0.0"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"a17t": "^0.10.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -0,0 +1,12 @@
|
|||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover" class="dark:bg-slate-900 bg-white text-zinc-900 dark:text-zinc-50">
|
||||
<div style="display: contents;">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<script lang="ts">
|
||||
export let tone = "~neutral";
|
||||
export let priority = "";
|
||||
</script>
|
||||
|
||||
<span class="badge {tone} {priority}"><slot/></span>
|
|
@ -0,0 +1,93 @@
|
|||
<script lang="ts">
|
||||
import {t} from "../scripts/i18n";
|
||||
import {theme} from "../stores/ThemeStore.js";
|
||||
import {get} from "svelte/store";
|
||||
import {onMount} from "svelte";
|
||||
import {getCurrentLocale, locales} from "../scripts/i18n.js";
|
||||
import {locale} from "../stores/LocaleStore";
|
||||
|
||||
function toggleTheme() {
|
||||
if ($theme === "dark") {
|
||||
theme.set("light");
|
||||
} else {
|
||||
theme.set("dark");
|
||||
}
|
||||
}
|
||||
|
||||
function setLocale(newLocale: string) {
|
||||
locale.set(newLocale);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
let dropdownOpen = false;
|
||||
|
||||
function toggleDropdown() {
|
||||
dropdownOpen = !dropdownOpen;
|
||||
}
|
||||
|
||||
function closeIfOpen() {
|
||||
if (dropdownOpen) {
|
||||
dropdownOpen = !dropdownOpen;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<nav class="flex items-center justify-between flex-wrap slate-300 dark:bg-slate-800 p-6 shadow-md">
|
||||
<span class="font-semibold text-xl tracking-tight">{t("common.appName")}</span>
|
||||
|
||||
<div class="inline-block text-sm leading-none lg:m-0">
|
||||
<div class="relative inline-block">
|
||||
<div>
|
||||
<button on:click={toggleDropdown} type="button"
|
||||
class="inline-flex w-full justify-center rounded-md px-4 py-2" id="menu-button"
|
||||
aria-expanded="true" aria-haspopup="true" title="{t('header.changeLang')}">
|
||||
<i class="fa-solid fa-language"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class:hidden={!dropdownOpen}
|
||||
class="transition absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-slate-100 dark:bg-slate-700 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||
role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1" on:mouseleave={closeIfOpen}>
|
||||
<div class="p-2" role="none">
|
||||
|
||||
<div class="rounded p-2 bg-slate-200 dark:bg-slate-600" role="menuitem" tabindex="-1">
|
||||
<div>
|
||||
<span class="mr-3 fi fi-{t('common.flag')}"></span>
|
||||
<span class="mt-0.2">{t("common.localeName")} - {t("common.selected")}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#each Object.keys(locales) as locale, i}
|
||||
{#if getCurrentLocale() != locale}
|
||||
<div class="rounded p-2 mt-2 hover:bg-slate-300 hover:dark:bg-slate-800 transition"
|
||||
role="menuitem" tabindex="-1" on:click={() => {setLocale(locale)}}>
|
||||
<div>
|
||||
<span class="mr-3 fi fi-{t('common.flag', locale)}"></span>
|
||||
<span class="mt-0.2">{t("common.localeName", locale)}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{#if $theme === "dark"}
|
||||
<button title="{t('header.lightMode')}" class="inline-block text-sm px-4 leading-none mt-4 lg:mt-0"
|
||||
on:click={toggleTheme}>
|
||||
<i class="fa-solid fa-sun"></i>
|
||||
</button>
|
||||
{:else}
|
||||
<button title="{t('header.darkMode')}" class="inline-block text-sm px-4 leading-none mt-4 lg:mt-0"
|
||||
on:click={toggleTheme}>
|
||||
<i class="fa-solid fa-moon"></i>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
</nav>
|
||||
</header>
|
|
@ -0,0 +1,24 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte";
|
||||
import {Logger, logSetup} from "../scripts/logger";
|
||||
import {theme} from "../stores/ThemeStore";
|
||||
import {get} from "svelte/store";
|
||||
|
||||
onMount(() => {
|
||||
let logger = new Logger("Theme.svelte");
|
||||
logSetup();
|
||||
|
||||
theme.subscribe((newTheme) => {
|
||||
if (newTheme === "dark") {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="stylesheet" href="https://cdn.e3t.cc/fa/6.2.0/css/all.min.css"/>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/flag-icons@6.6.6/css/flag-icons.min.css"/>
|
||||
</svelte:head>
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"common": {
|
||||
"localeName": "English",
|
||||
"appName": "trifid",
|
||||
"flag": "gb",
|
||||
"selected": "selected"
|
||||
},
|
||||
"header": {
|
||||
"lightMode": "Light mode",
|
||||
"darkMode": "Dark mode",
|
||||
"changeLang": "Change language"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"common": {
|
||||
"localeName": "日本語",
|
||||
"appName": "trifid",
|
||||
"flag": "jp",
|
||||
"selected": "選択済み"
|
||||
},
|
||||
"header": {
|
||||
"lightMode": "ライトモード",
|
||||
"darkMode": "ダークモード",
|
||||
"changeLang": "言語の変更"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"common": {
|
||||
"localeName": "Nederlands",
|
||||
"appName": "trifid",
|
||||
"flag": "nl",
|
||||
"selected": "Geselecteerd"
|
||||
},
|
||||
"header": {
|
||||
"lightMode": "Lichtmodus",
|
||||
"darkMode": "Donkere modus",
|
||||
"changeLang": "Taal wijzigen"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import "../app.css";
|
||||
|
||||
import Theme from '../components/Theme.svelte';
|
||||
import Header from "../components/Header.svelte";
|
||||
</script>
|
||||
|
||||
<Theme/>
|
||||
|
||||
<Header/>
|
||||
<slot />
|
|
@ -0,0 +1,3 @@
|
|||
<script>
|
||||
import Header from "../../../components/Header.svelte";
|
||||
</script>
|
|
@ -0,0 +1,11 @@
|
|||
import type { Writable } from "svelte/store";
|
||||
import {writable} from "svelte/store";
|
||||
import {browser} from "$app/environment";
|
||||
|
||||
export function persist(name: string, def_val = ""): Writable<any> {
|
||||
const store = writable(browser && localStorage.getItem(name) || def_val);
|
||||
store.subscribe((value: any) => {
|
||||
if (browser) return (localStorage.setItem(name, value));
|
||||
});
|
||||
return store;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import {locale} from "../stores/LocaleStore";
|
||||
import {get} from "svelte/store";
|
||||
|
||||
import en from "../i18n/en.json";
|
||||
import jp from "../i18n/jp.json";
|
||||
import nl from "../i18n/nl.json";
|
||||
import {Logger, logSetup} from "./logger";
|
||||
|
||||
logSetup();
|
||||
const logger = new Logger("i18n");
|
||||
|
||||
export const locales: any = {
|
||||
"en": en,
|
||||
"jp": jp,
|
||||
"nl": nl
|
||||
}
|
||||
|
||||
export function getCurrentLocale(): string {
|
||||
return get(locale);
|
||||
}
|
||||
|
||||
export function setLocale(newLocale: string) {
|
||||
logger.info(`Setting locale to ${newLocale}`);
|
||||
if (!(newLocale in locales)) {
|
||||
logger.error(`Locale ${newLocale} does not exist`);
|
||||
return false;
|
||||
}
|
||||
locale.set(newLocale);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function t(query: string, locale = ""): string {
|
||||
let val: any = locales[locale === "" ? getCurrentLocale() : locale];
|
||||
const spl: Array<string> = query.split('.');
|
||||
for (let i = 0; i < spl.length; i++) {
|
||||
const item = spl[i];
|
||||
if (item in val) {
|
||||
val = val[item];
|
||||
} else {
|
||||
logger.info(`missing locale string ${query}`);
|
||||
val = "missing: " + query;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (typeof val === "object") {
|
||||
logger.info(`query string ${query} is not a message`);
|
||||
return "missing: " + query;
|
||||
}
|
||||
return val;
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
// LOG LEVELS:
|
||||
// 0: Fatal
|
||||
// 1: Error
|
||||
// 2: Warn
|
||||
// 3: Info
|
||||
// 4: Debug
|
||||
// Logs are written in-memory and logged to console if logLevel > showLogLevel
|
||||
let loglevel = 0;
|
||||
|
||||
export const LEVEL_FATAL = 0;
|
||||
export const LEVEL_ERROR = 1;
|
||||
export const LEVEL_WARN = 2;
|
||||
export const LEVEL_INFO = 3;
|
||||
export const LEVEL_DEBUG = 4;
|
||||
|
||||
interface LogLine {
|
||||
level: number,
|
||||
module: string,
|
||||
message: string,
|
||||
timestamp: number,
|
||||
deltaStart: number,
|
||||
deltaLast: number
|
||||
}
|
||||
|
||||
const debugLog: Array<LogLine> = [];
|
||||
let timestampStart: number;
|
||||
let deltaTimestamp: number;
|
||||
let logger: Logger;
|
||||
let loggerSetup = false;
|
||||
|
||||
export function logSetup() {
|
||||
if (loggerSetup) return;
|
||||
loggerSetup = true;
|
||||
timestampStart = Date.now();
|
||||
deltaTimestamp = Date.now();
|
||||
logger = new Logger("Logger.ts");
|
||||
consoleLogLevel(LEVEL_INFO);
|
||||
logger.info("Logger setup complete");
|
||||
}
|
||||
|
||||
function log(level: number, module: string, message: string) {
|
||||
const log = {
|
||||
level: level,
|
||||
module: module,
|
||||
message: message,
|
||||
timestamp: Date.now(),
|
||||
deltaStart: Date.now() - timestampStart,
|
||||
deltaLast: Date.now() - deltaTimestamp
|
||||
};
|
||||
deltaTimestamp = Date.now();
|
||||
|
||||
debugLog.push(log);
|
||||
|
||||
consoleLogHandler(log);
|
||||
}
|
||||
|
||||
function consoleLogHandler(log: LogLine) {
|
||||
if (import.meta.env.SSR) return; // no logging in ssr
|
||||
if (log.level <= loglevel) {
|
||||
let textstyle = "";
|
||||
switch (log.level) {
|
||||
case 0:
|
||||
textstyle = "color:red;font-weight:bold;";
|
||||
break;
|
||||
case 1:
|
||||
textstyle = "color:red;";
|
||||
break;
|
||||
case 2:
|
||||
textstyle = "color:yellow;";
|
||||
break;
|
||||
case 3:
|
||||
textstyle = "";
|
||||
break;
|
||||
case 4:
|
||||
textstyle = "color:gray;";
|
||||
break;
|
||||
}
|
||||
console.log(`%c${new Date(log.timestamp).toISOString()} ${(log.deltaStart / 1000).toFixed(3)}Σ ${(log.deltaLast / 1000).toFixed(3)}Δ\n%c[${log.module}]%c ${log.message}`, "color:gray", "color:#8c188c", textstyle);
|
||||
}
|
||||
}
|
||||
|
||||
export function consoleLogLevel(level: number) {
|
||||
loglevel = level;
|
||||
}
|
||||
|
||||
export function consoleLogDump() {
|
||||
for (let i = 0; i < debugLog.length; i++) {
|
||||
consoleLogHandler(debugLog[i]);
|
||||
}
|
||||
}
|
||||
|
||||
export class Logger {
|
||||
private name: string;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
fatal(message: any) {
|
||||
log(LEVEL_FATAL, this.name, message)
|
||||
}
|
||||
|
||||
error(message: any) {
|
||||
log(LEVEL_ERROR, this.name, message)
|
||||
}
|
||||
|
||||
warn(message: any) {
|
||||
log(LEVEL_WARN, this.name, message)
|
||||
}
|
||||
|
||||
info(message: any) {
|
||||
log(LEVEL_INFO, this.name, message)
|
||||
}
|
||||
|
||||
debug(message: any) {
|
||||
log(LEVEL_DEBUG, this.name, message)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import { persist } from "../scripts/PersistentStore";
|
||||
|
||||
export const locale = persist("lang", "en");
|
|
@ -0,0 +1,3 @@
|
|||
import { persist } from "../scripts/PersistentStore";
|
||||
|
||||
export const theme = persist("theme", "light");
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,15 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -0,0 +1,22 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
|
||||
const colors = require("tailwindcss/colors.js");
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
neutral: colors.slate,
|
||||
positive: colors.green,
|
||||
urge: colors.violet,
|
||||
warning: colors.yellow,
|
||||
info: colors.blue,
|
||||
critical: colors.red
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('a17t')
|
||||
],
|
||||
darkMode: 'class'
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true
|
||||
}
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()]
|
||||
});
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue