Bundle modules together into one

Fixes #26.
This commit is contained in:
Florine W. Dekker 2022-11-20 21:18:46 +01:00
parent 70465ef7bd
commit 9beda27c23
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
8 changed files with 77 additions and 139 deletions

View File

@ -14,33 +14,23 @@ module.exports = grunt => {
}, },
}, },
focus: { focus: {
deploy: { dev: {
include: ["css", "storage", "template", "validation"], include: ["css", "ts"],
},
},
watch: {
css: {
files: ["src/main/**/*.css"],
tasks: ["cssmin"],
},
ts: {
files: ["src/main/**/*.ts"],
tasks: ["webpack:dev"],
}, },
}, },
webpack: { webpack: {
storage: { options: {
entry: "./src/main/js/Storage.ts", entry: "./src/main/js/Main.ts",
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".ts"],
},
output: {
filename: "storage.js",
path: path.resolve(__dirname, "dist/"),
},
mode: "production",
},
template: {
entry: "./src/main/js/Template.ts",
module: { module: {
rules: [ rules: [
{ {
@ -55,49 +45,17 @@ module.exports = grunt => {
}, },
output: { output: {
filename: "template.js", filename: "template.js",
path: path.resolve(__dirname, "dist"),
},
mode: "production",
},
validation: {
entry: "./src/main/js/Validation.ts",
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".ts"],
},
output: {
filename: "validation.js",
path: path.resolve(__dirname, "dist/"), path: path.resolve(__dirname, "dist/"),
}, },
},
dev: {
mode: "development",
devtool: "inline-source-map",
},
deploy: {
mode: "production", mode: "production",
}, },
}, },
watch: {
css: {
files: ["src/main/**/*.css"],
tasks: ["cssmin"],
},
storage: {
files: ["src/main/js/Storage.ts"],
tasks: ["webpack:storage"],
},
template: {
files: ["src/main/js/Template.ts"],
tasks: ["webpack:template"],
},
validation: {
files: ["src/main/js/Validation.ts"],
tasks: ["webpack:validation"],
},
},
}); });
grunt.loadNpmTasks("grunt-contrib-clean"); grunt.loadNpmTasks("grunt-contrib-clean");
@ -106,8 +64,9 @@ module.exports = grunt => {
grunt.loadNpmTasks("grunt-focus"); grunt.loadNpmTasks("grunt-focus");
grunt.loadNpmTasks("grunt-webpack"); grunt.loadNpmTasks("grunt-webpack");
grunt.registerTask("deploy", ["webpack:storage", "webpack:template", "webpack:validation", "cssmin"]); grunt.registerTask("dev", ["clean", "webpack:dev", "cssmin"]);
grunt.registerTask("deploy:server", ["deploy", "focus:deploy"]); grunt.registerTask("dev:server", ["dev", "focus:dev"]);
grunt.registerTask("deploy", ["clean", "webpack:deploy", "cssmin"]);
grunt.registerTask("default", ["deploy"]); grunt.registerTask("default", ["dev"]);
}; };

View File

@ -4,16 +4,6 @@ The base template for pages on fwdekker.com.
This module contains templating functions (e.g. `nav`, `footer`), CSS libraries, and some common utility methods that This module contains templating functions (e.g. `nav`, `footer`), CSS libraries, and some common utility methods that
are used on nearly all pages anyway. are used on nearly all pages anyway.
The main functionality is provided in `template.js` and `template.css`.
There also exist optional modules for easily reusing common code.
Modules can be used stand-alone.
If `template.js` is used, modules should be loaded after `template.js`.
Current available modules are:
* `storage.js` for interfacing with local storage, and
Main module optional.
* `validation.js` for form validation.
Requires main module.
## Development ## Development
### Requirements ### Requirements
@ -27,8 +17,10 @@ $> npm ci
### Building ### Building
```shell script ```shell script
# Build the template in `dist/` for deployment # Build the tool in `dist/` for development
$> npm run dev
# Same as above, but automatically rerun it whenever files are changed
$> npm run dev:server
# Build the tool in `dist/` for deployment
$> npm run deploy $> npm run deploy
# Run the `deploy` task and automatically rerun it whenever files are changed
$> npm run deploy:server
``` ```

View File

@ -14,14 +14,14 @@
}, },
"browser": "template.js", "browser": "template.js",
"files": [ "files": [
"dist/storage.js",
"dist/template.js", "dist/template.js",
"dist/template.css" "dist/template.css"
], ],
"scripts": { "scripts": {
"clean": "grunt clean", "clean": "grunt clean",
"deploy": "grunt deploy", "dev": "grunt dev",
"deploy:server": "grunt deploy:server" "dev:server": "grunt dev:server",
"deploy": "grunt deploy"
}, },
"dependencies": { "dependencies": {
"@picocss/pico": "^1.5.6" "@picocss/pico": "^1.5.6"

7
src/main/js/Main.ts Normal file
View File

@ -0,0 +1,7 @@
import * as template from "./Template";
import * as storage from "./Storage";
import * as validation from "./Validation";
(window as any).fwdekker = template;
(window as any).fwdekker.storage = storage;
(window as any).fwdekker.validation = validation;

View File

@ -5,7 +5,7 @@ export interface Storage {
/** /**
* Removes the data from storage. * Removes the data from storage.
*/ */
clear(): void clear(): void;
/** /**
* Retrieves an array from storage. * Retrieves an array from storage.
@ -13,7 +13,7 @@ export interface Storage {
* @param name the name of the array to retrieve * @param name the name of the array to retrieve
* @param def the value to return if no array is stored with the given name * @param def the value to return if no array is stored with the given name
*/ */
getArray(name: string, def: any[]): any[] getArray(name: string, def: any[]): any[];
/** /**
* Stores an array. * Stores an array.
@ -22,7 +22,7 @@ export interface Storage {
* @param value the array to store under the given name * @param value the array to store under the given name
* @protected * @protected
*/ */
setArray(name: string, value: any[]): void setArray(name: string, value: any[]): void;
/** /**
* Retrieves a boolean from storage. * Retrieves a boolean from storage.
@ -31,7 +31,7 @@ export interface Storage {
* @param def the value to return if no boolean is stored with the given name * @param def the value to return if no boolean is stored with the given name
* @protected * @protected
*/ */
getBoolean(name: string, def: boolean): boolean getBoolean(name: string, def: boolean): boolean;
/** /**
* Stores a boolean. * Stores a boolean.
@ -40,7 +40,7 @@ export interface Storage {
* @param value the boolean to store under the given name * @param value the boolean to store under the given name
* @protected * @protected
*/ */
setBoolean(name: string, value: boolean): void setBoolean(name: string, value: boolean): void;
/** /**
* Retrieves a number from storage. * Retrieves a number from storage.
@ -49,7 +49,7 @@ export interface Storage {
* @param def the value to return if no number is stored with the given name * @param def the value to return if no number is stored with the given name
* @protected * @protected
*/ */
getNumber(name: string, def: number): number getNumber(name: string, def: number): number;
/** /**
* Stores a number. * Stores a number.
@ -58,7 +58,7 @@ export interface Storage {
* @param value the number to store under the given name * @param value the number to store under the given name
* @protected * @protected
*/ */
setNumber(name: string, value: number): void setNumber(name: string, value: number): void;
} }
/** /**
@ -176,9 +176,3 @@ export class MemoryStorage implements Storage {
return this.storage[name] ?? def; return this.storage[name] ?? def;
} }
} }
// Export to `window`
(window as any).fwdekker = (window as any).fwdekker ?? {};
(window as any).fwdekker.storage = {Storage, LocalStorage, MemoryStorage};
export {};

View File

@ -5,7 +5,7 @@
* @param query the type of element to return * @param query the type of element to return
* @returns the HTML element described by the given string, or `null` if `query` did not match any element * @returns the HTML element described by the given string, or `null` if `query` did not match any element
*/ */
function stringToHtml(string: string, query: string): HTMLElement | null { export function stringToHtml(string: string, query: string): HTMLElement | null {
return (new DOMParser()).parseFromString(string, "text/html").body.querySelector(query); return (new DOMParser()).parseFromString(string, "text/html").body.querySelector(query);
} }
@ -16,7 +16,7 @@ function stringToHtml(string: string, query: string): HTMLElement | null {
* @param root the element to start searching in, or `undefined` if searching should start in `document` * @param root the element to start searching in, or `undefined` if searching should start in `document`
* @returns the element identified by `query` in `root`, or `null` if that element could not be found * @returns the element identified by `query` in `root`, or `null` if that element could not be found
*/ */
function $(query: string | null | undefined, root?: HTMLElement): HTMLElement | null { export function $(query: string | null | undefined, root?: HTMLElement): HTMLElement | null {
if (query == null) return null; if (query == null) return null;
return root === undefined ? document.querySelector(query) : root.querySelector(query); return root === undefined ? document.querySelector(query) : root.querySelector(query);
} }
@ -28,7 +28,7 @@ function $(query: string | null | undefined, root?: HTMLElement): HTMLElement |
* @param root the element to start searching in, or `undefined` if searching should start in `document` * @param root the element to start searching in, or `undefined` if searching should start in `document`
* @returns the elements identified by `query` in `root` * @returns the elements identified by `query` in `root`
*/ */
function $a(query: string, root?: HTMLElement): NodeListOf<Element> { export function $a(query: string, root?: HTMLElement): NodeListOf<Element> {
return root === undefined ? document.querySelectorAll(query) : root.querySelectorAll(query); return root === undefined ? document.querySelectorAll(query) : root.querySelectorAll(query);
} }
@ -40,7 +40,7 @@ function $a(query: string, root?: HTMLElement): NodeListOf<Element> {
* *
* @param fun the function to run * @param fun the function to run
*/ */
function doAfterLoad(fun: () => void): void { export function doAfterLoad(fun: () => void): void {
if (document.readyState === "complete") { if (document.readyState === "complete") {
fun(); fun();
return; return;
@ -63,7 +63,7 @@ function doAfterLoad(fun: () => void): void {
* @return the `content` attribute of the `<meta>` tag identified by `name`, or `null` if the meta tag has no content, * @return the `content` attribute of the `<meta>` tag identified by `name`, or `null` if the meta tag has no content,
* or `undefined` if the meta tag does not exist * or `undefined` if the meta tag does not exist
*/ */
function getMetaProperty(name: string): string | null | undefined { export function getMetaProperty(name: string): string | null | undefined {
const metaTag = $(`meta[name="${name}"]`); const metaTag = $(`meta[name="${name}"]`);
return metaTag == null ? undefined : metaTag.getAttribute("content"); return metaTag == null ? undefined : metaTag.getAttribute("content");
} }
@ -252,8 +252,3 @@ doAfterLoad(() => {
); );
} }
}); });
// Export to `window`
(window as any).fwdekker = {$, $a, doAfterLoad, getMetaProperty, stringToHtml};
export {};

View File

@ -1,5 +1,4 @@
if ((window as any).fwdekker == null) throw new Error("Validation module requires main module."); import {$, $a, doAfterLoad, getMetaProperty} from "./Template";
const {$, $a, doAfterLoad, getMetaProperty} = (window as any).fwdekker;
/** /**
@ -7,9 +6,13 @@ const {$, $a, doAfterLoad, getMetaProperty} = (window as any).fwdekker;
* *
* @param form the form to hide validation information from * @param form the form to hide validation information from
*/ */
function clearFormValidity(form: HTMLFormElement): void { export function clearFormValidity(form: HTMLFormElement): void {
clearMessageStatus(form); clearMessageStatus(form);
$a("input", form).forEach((input: HTMLInputElement) => clearInputValidity(input)); $a("input", form).forEach((input: Element) => {
if (!(input instanceof HTMLInputElement)) return;
clearInputValidity(input);
});
} }
@ -20,22 +23,21 @@ function clearFormValidity(form: HTMLFormElement): void {
* @param message the message to show in `card`, or `undefined` if `card` should be hidden * @param message the message to show in `card`, or `undefined` if `card` should be hidden
* @param type the type of message to show in `card`, or `undefined` if `card` should be hidden * @param type the type of message to show in `card`, or `undefined` if `card` should be hidden
*/ */
function showMessageType(card: HTMLElement | HTMLFormElement, export function showMessageType(card: HTMLElement | HTMLFormElement,
message?: string, message?: string,
type?: "error" | "info" | "success" | "warning"): void { type?: "error" | "info" | "success" | "warning"): void {
if (card instanceof HTMLFormElement) { if (card instanceof HTMLFormElement)
card = $(`article[data-status-for="${card.id}"]`); card = $(`article[data-status-for="${card.id}"]`)!;
if (card == null) throw new Error("Could not find status card."); const output = $("output", card)!;
}
card.classList.remove("hidden", "error", "info", "success", "warning"); card.classList.remove("hidden", "error", "info", "success", "warning");
if (message == null || type == null) { if (message == null || type == null) {
card.classList.add("hidden"); card.classList.add("hidden");
$("output", card).innerText = ""; output.innerText = "";
} else { } else {
card.classList.add(type); card.classList.add(type);
$("output", card).innerText = message; output.innerText = message;
} }
} }
@ -44,7 +46,7 @@ function showMessageType(card: HTMLElement | HTMLFormElement,
* *
* @param card the card to clear the message from * @param card the card to clear the message from
*/ */
function clearMessageStatus(card: HTMLElement): void { export function clearMessageStatus(card: HTMLElement): void {
showMessageType(card); showMessageType(card);
} }
@ -54,7 +56,7 @@ function clearMessageStatus(card: HTMLElement): void {
* @param card the card to show `message` in * @param card the card to show `message` in
* @param message the error message to show in `card` * @param message the error message to show in `card`
*/ */
function showMessageError(card: HTMLElement, message: string): void { export function showMessageError(card: HTMLElement, message: string): void {
showMessageType(card, message, "error"); showMessageType(card, message, "error");
} }
@ -64,7 +66,7 @@ function showMessageError(card: HTMLElement, message: string): void {
* @param card the card to show `message` in * @param card the card to show `message` in
* @param message the message to show in `card` * @param message the message to show in `card`
*/ */
function showMessageInfo(card: HTMLElement, message: string): void { export function showMessageInfo(card: HTMLElement, message: string): void {
showMessageType(card, message, "info"); showMessageType(card, message, "info");
} }
@ -74,7 +76,7 @@ function showMessageInfo(card: HTMLElement, message: string): void {
* @param card the card to show `message` in * @param card the card to show `message` in
* @param message the success message to show in `card` * @param message the success message to show in `card`
*/ */
function showMessageSuccess(card: HTMLElement, message: string): void { export function showMessageSuccess(card: HTMLElement, message: string): void {
showMessageType(card, message, "success"); showMessageType(card, message, "success");
} }
@ -84,7 +86,7 @@ function showMessageSuccess(card: HTMLElement, message: string): void {
* @param card the card to show `message` in * @param card the card to show `message` in
* @param message the success message to show in `card` * @param message the success message to show in `card`
*/ */
function showMessageWarning(card: HTMLElement, message: string): void { export function showMessageWarning(card: HTMLElement, message: string): void {
showMessageType(card, message, "warning"); showMessageType(card, message, "warning");
} }
@ -94,7 +96,7 @@ function showMessageWarning(card: HTMLElement, message: string): void {
* *
* @param input * @param input
*/ */
function clearInputValidity(input: HTMLInputElement): void { export function clearInputValidity(input: HTMLInputElement): void {
input.classList.remove("valid", "invalid"); input.classList.remove("valid", "invalid");
input.removeAttribute("aria-invalid"); input.removeAttribute("aria-invalid");
input.removeAttribute("aria-errormessage"); input.removeAttribute("aria-errormessage");
@ -118,7 +120,7 @@ function clearInputValidity(input: HTMLInputElement): void {
* @param input the input to show as invalid * @param input the input to show as invalid
* @param message the message explaining what is invalid * @param message the message explaining what is invalid
*/ */
function showInputInvalid(input: HTMLInputElement, message?: string): void { export function showInputInvalid(input: HTMLInputElement, message?: string): void {
clearInputValidity(input); clearInputValidity(input);
input.classList.add("invalid"); input.classList.add("invalid");
@ -145,7 +147,7 @@ function showInputInvalid(input: HTMLInputElement, message?: string): void {
* @param input the input to show as valid * @param input the input to show as valid
* @param message the message to show at the input * @param message the message to show at the input
*/ */
function showInputValid(input: HTMLInputElement, message?: string): void { export function showInputValid(input: HTMLInputElement, message?: string): void {
clearInputValidity(input); clearInputValidity(input);
input.classList.add("valid"); input.classList.add("valid");
@ -170,7 +172,9 @@ function showInputValid(input: HTMLInputElement, message?: string): void {
doAfterLoad(() => { doAfterLoad(() => {
if (getMetaProperty("fwd:validation:load-forms") === undefined) return; if (getMetaProperty("fwd:validation:load-forms") === undefined) return;
$a(".status-card .close").forEach((close: HTMLElement) => { $a(".status-card .close").forEach((close: Element) => {
if (!(close instanceof HTMLElement)) return;
close.addEventListener("click", (event: MouseEvent) => { close.addEventListener("click", (event: MouseEvent) => {
event.preventDefault(); event.preventDefault();
@ -184,13 +188,3 @@ doAfterLoad(() => {
hint.innerText = hint.dataset["hint"] ?? ""; hint.innerText = hint.dataset["hint"] ?? "";
}); });
}); });
// Export to `window`
(window as any).fwdekker = (window as any).fwdekker ?? {};
(window as any).fwdekker.validation = {
clearFormValidity,
clearMessageStatus, showMessageError, showMessageInfo, showMessageSuccess, showMessageWarning,
clearInputValidity, showInputInvalid, showInputValid
};
export {};

View File

@ -95,9 +95,6 @@
<!--suppress HtmlUnknownTarget --> <!--suppress HtmlUnknownTarget -->
<script src="../../dist/template.js"></script> <script src="../../dist/template.js"></script>
<script src="../../dist/storage.js"></script>
<script src="../../dist/validation.js"></script>
<script> <script>
const {$} = window.fwdekker; const {$} = window.fwdekker;