add initial project for frontend
Signed-off-by: Tobias Erbshäußer <tobias@tesoft.dev>
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
distMinified
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
Generated
+2904
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build && html-minifier-terser --collapse-whitespace --remove-comments --input-dir ./dist --file-ext html --minify-css --output-dir ./distMinified && cp -r ./dist/assets ./distMinified",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vituum/vite-plugin-nunjucks": "^1.1.0",
|
||||||
|
"html-minifier-terser": "^7.2.0",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.4.6",
|
||||||
|
"vite-plugin-checker": "^0.8.0",
|
||||||
|
"vituum": "^1.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@directus/sdk": "^17.0.0",
|
||||||
|
"@types/dompurify": "^3.0.5",
|
||||||
|
"dompurify": "^3.1.6",
|
||||||
|
"marked": "^14.1.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<template id="tesoft-button-template">
|
||||||
|
<style>
|
||||||
|
@import "src/styles/default.css";
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<button>
|
||||||
|
<slot></slot>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="/src/components/button.ts" type="module"></script>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import {TesoftComponent} from "../scripts/main.ts";
|
||||||
|
|
||||||
|
export class TesoftButton extends TesoftComponent {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const template = document.getElementById("tesoft-button-template") as HTMLTemplateElement;
|
||||||
|
const templateContent = template.content;
|
||||||
|
|
||||||
|
const shadowRoot = this.attachShadow({mode: "open"});
|
||||||
|
shadowRoot.appendChild(templateContent.cloneNode(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("tesoft-button", TesoftButton);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<link rel="stylesheet" href="/src/styles/main.css">
|
||||||
|
{% block styles %}
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{% block components %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block sources %}
|
||||||
|
{% endblock %}
|
||||||
|
<script src="/src/scripts/main.ts" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{% extends 'layouts/main.njk' %}
|
||||||
|
|
||||||
|
{% set title='TESOFT - Home' %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
<link rel="stylesheet" href="/src/styles/index.css">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block sources %}
|
||||||
|
<script src="/src/scripts/index.ts" type="module"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block components %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
export abstract class TesoftComponent extends HTMLElement {
|
||||||
|
private ready: boolean;
|
||||||
|
|
||||||
|
protected constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.ready = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
async connectedCallback() {
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
for (const child of findComponents(this.shadowRoot)) {
|
||||||
|
promises.push(new Promise(resolve =>
|
||||||
|
window.customElements.whenDefined(child.tagName.toLowerCase()).then(() => {
|
||||||
|
if (child.isConnected) {
|
||||||
|
resolve(void {});
|
||||||
|
} else {
|
||||||
|
child.addEventListener("ready", (e: any) => {
|
||||||
|
e.preventDefault();
|
||||||
|
resolve(void {});
|
||||||
|
}, {once: true});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
await this.populate();
|
||||||
|
|
||||||
|
this.ready = true;
|
||||||
|
this.dispatchEvent(new CustomEvent("ready", {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async populate() {
|
||||||
|
// Overwrite if necessary in components
|
||||||
|
}
|
||||||
|
|
||||||
|
get isReady(): boolean {
|
||||||
|
return this.ready;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findComponents(root: any | null): TesoftComponent[] {
|
||||||
|
const components = [];
|
||||||
|
|
||||||
|
for (const child of root?.querySelectorAll("*") ?? []) {
|
||||||
|
if (child.tagName.toLowerCase().startsWith("snz-")) {
|
||||||
|
components.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onReady(callback: () => void, ...components: TesoftComponent[]) {
|
||||||
|
async function domLoaded() {
|
||||||
|
if (components.length === 0) {
|
||||||
|
components = findComponents(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises = [];
|
||||||
|
for (const component of components) {
|
||||||
|
if (!component.isReady) {
|
||||||
|
promises.push(new Promise(resolve => {
|
||||||
|
component.addEventListener("ready", resolve, {once: true});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", domLoaded, {once: true});
|
||||||
|
} else {
|
||||||
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
|
domLoaded();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
:root {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
/* Set font-size so that 1rem = 10 px */
|
||||||
|
font-size: 62.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
line-height: 2.1rem;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@import "default.css";
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": [
|
||||||
|
"ES2020",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import vituum from "vituum"
|
||||||
|
import nunjucks from "@vituum/vite-plugin-nunjucks"
|
||||||
|
import checker from "vite-plugin-checker";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
publicDir: "public",
|
||||||
|
plugins: [
|
||||||
|
vituum(),
|
||||||
|
nunjucks({
|
||||||
|
root: "./src",
|
||||||
|
extensions: {
|
||||||
|
IncludeOnceExtension: includeOnceExtension,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
checker({
|
||||||
|
typescript: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
server: {
|
||||||
|
cors: {
|
||||||
|
"origin": "*",
|
||||||
|
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
|
||||||
|
"preflightContinue": false,
|
||||||
|
"optionsSuccessStatus": 204
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:8055',
|
||||||
|
changeOrigin: false,
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function includeOnceExtension() {
|
||||||
|
this.tagName = "includeOnce";
|
||||||
|
this.tags = [this.tagName];
|
||||||
|
this.alreadyIncluded = new Set();
|
||||||
|
|
||||||
|
this.parse = function (parser, nodes, _lexer) {
|
||||||
|
const tag = parser.peekToken();
|
||||||
|
if (!parser.skipSymbol(this.tagName)) {
|
||||||
|
parser.fail("parseInclude: expected " + this.tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = new nodes.Include(tag.lineno, tag.colno);
|
||||||
|
node.template = parser.parseExpression();
|
||||||
|
|
||||||
|
if (parser.skipSymbol("ignore") && parser.skipSymbol("missing")) {
|
||||||
|
node.ignoreMissing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.advanceAfterBlockEnd(tag.value);
|
||||||
|
|
||||||
|
const includedPath = node.template.value;
|
||||||
|
if (this.alreadyIncluded.has(includedPath)) {
|
||||||
|
return new nodes.NodeList(0, 0, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.alreadyIncluded.add(includedPath);
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user