Major frontend code cleanup (#452)

Many large changes, including:
- TypeScript enums are now string unions throughout
- Strong type-checking throughout the TS and Vue codebase
- Vue component props now all specify `as PropType<...>`
- Usage of annotated return types on all functions
- Sorting of JS import statements
- Explicit usage of Vue bind attribute function call arguments (`@click="foo"` is now `@click=(e) => foo(e)`)
- Much improved code quality related to the color picker
- Consistent camelCase Vue bind and v-model attributes
- Consistent Vue HTML attribute strings with single quotes
- Bug fix and clarity improvement with incorrect hint class parameters
- Empty Vue component objects like `props: {}` and `components: {}` removed
This commit is contained in:
Keavon Chambers
2022-01-02 06:00:02 -08:00
parent 6662a9a04f
commit 2c8d70acb4
53 changed files with 842 additions and 946 deletions
+6 -5
View File
@@ -10,23 +10,24 @@ const GRAPHITE_AUTO_SAVE_ORDER_KEY = "auto-save-documents-order";
const databaseConnection: Promise<IDBDatabase> = new Promise((resolve) => {
const dbOpenRequest = indexedDB.open(GRAPHITE_INDEXED_DB_NAME, GRAPHITE_INDEXED_DB_VERSION);
dbOpenRequest.onupgradeneeded = () => {
dbOpenRequest.onupgradeneeded = (): void => {
const db = dbOpenRequest.result;
if (!db.objectStoreNames.contains(GRAPHITE_AUTO_SAVE_STORE)) {
db.createObjectStore(GRAPHITE_AUTO_SAVE_STORE, { keyPath: "details.id" });
}
};
dbOpenRequest.onerror = () => {
dbOpenRequest.onerror = (): void => {
// eslint-disable-next-line no-console
console.error("Graphite IndexedDb error:", dbOpenRequest.error);
};
dbOpenRequest.onsuccess = () => {
dbOpenRequest.onsuccess = (): void => {
resolve(dbOpenRequest.result);
};
});
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function createAutoSaveManager(editor: EditorState, documents: DocumentsState) {
const openAutoSavedDocuments = async (): Promise<void> => {
const db = await databaseConnection;
@@ -34,7 +35,7 @@ export function createAutoSaveManager(editor: EditorState, documents: DocumentsS
const request = transaction.objectStore(GRAPHITE_AUTO_SAVE_STORE).getAll();
return new Promise((resolve) => {
request.onsuccess = () => {
request.onsuccess = (): void => {
const previouslySavedDocuments: AutoSaveDocument[] = request.result;
const documentOrder: string[] = JSON.parse(window.localStorage.getItem(GRAPHITE_AUTO_SAVE_ORDER_KEY) || "[]");
@@ -48,7 +49,7 @@ export function createAutoSaveManager(editor: EditorState, documents: DocumentsS
});
};
const storeDocumentOrder = () => {
const storeDocumentOrder = (): void => {
// Make sure to store as string since JSON does not play nice with BigInt
const documentOrder = documents.state.documents.map((doc) => doc.id.toString());
window.localStorage.setItem(GRAPHITE_AUTO_SAVE_ORDER_KEY, JSON.stringify(documentOrder));
+4 -4
View File
@@ -1,10 +1,10 @@
import { DialogState } from "@/state/dialog";
import { TextButtonWidget } from "@/components/widgets/widgets";
import { DisplayError, DisplayPanic } from "@/dispatcher/js-messages";
import { DialogState } from "@/state/dialog";
import { EditorState } from "@/state/wasm-loader";
import { stripIndents } from "@/utilities/strip-indents";
import { TextButtonWidget } from "@/utilities/widgets";
export function initErrorHandling(editor: EditorState, dialogState: DialogState) {
export function initErrorHandling(editor: EditorState, dialogState: DialogState): void {
// Graphite error dialog
editor.dispatcher.subscribeJsMessage(DisplayError, (displayError) => {
const okButton: TextButtonWidget = {
@@ -49,7 +49,7 @@ export function initErrorHandling(editor: EditorState, dialogState: DialogState)
});
}
function githubUrl(panicDetails: string) {
function githubUrl(panicDetails: string): string {
const url = new URL("https://github.com/GraphiteEditor/Graphite/issues/new");
const body = stripIndents`
+24 -23
View File
@@ -1,6 +1,6 @@
import { DialogState } from "@/state/dialog";
import { FullscreenState } from "@/state/fullscreen";
import { DocumentsState } from "@/state/documents";
import { FullscreenState } from "@/state/fullscreen";
import { EditorState } from "@/state/wasm-loader";
type EventName = keyof HTMLElementEventMap | keyof WindowEventHandlersEventMap;
@@ -9,20 +9,21 @@ interface EventListenerTarget {
removeEventListener: typeof window.removeEventListener;
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function createInputManager(editor: EditorState, container: HTMLElement, dialog: DialogState, document: DocumentsState, fullscreen: FullscreenState) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const listeners: { target: EventListenerTarget; eventName: EventName; action: (event: any) => void; options?: boolean | AddEventListenerOptions }[] = [
{ target: window, eventName: "resize", action: () => onWindowResize(container) },
{ target: window, eventName: "beforeunload", action: (e) => onBeforeUnload(e) },
{ target: window.document, eventName: "contextmenu", action: (e) => e.preventDefault() },
{ target: window.document, eventName: "fullscreenchange", action: () => fullscreen.fullscreenModeChanged() },
{ target: window, eventName: "keyup", action: (e) => onKeyUp(e) },
{ target: window, eventName: "keydown", action: (e) => onKeyDown(e) },
{ target: window, eventName: "pointermove", action: (e) => onPointerMove(e) },
{ target: window, eventName: "pointerdown", action: (e) => onPointerDown(e) },
{ target: window, eventName: "pointerup", action: (e) => onPointerUp(e) },
{ target: window, eventName: "mousedown", action: (e) => onMouseDown(e) },
{ target: window, eventName: "wheel", action: (e) => onMouseScroll(e), options: { passive: false } },
{ target: window, eventName: "resize", action: (): void => onWindowResize(container) },
{ target: window, eventName: "beforeunload", action: (e: BeforeUnloadEvent): void => onBeforeUnload(e) },
{ target: window.document, eventName: "contextmenu", action: (e: MouseEvent): void => e.preventDefault() },
{ target: window.document, eventName: "fullscreenchange", action: (): void => fullscreen.fullscreenModeChanged() },
{ target: window, eventName: "keyup", action: (e: KeyboardEvent): void => onKeyUp(e) },
{ target: window, eventName: "keydown", action: (e: KeyboardEvent): void => onKeyDown(e) },
{ target: window, eventName: "pointermove", action: (e: PointerEvent): void => onPointerMove(e) },
{ target: window, eventName: "pointerdown", action: (e: PointerEvent): void => onPointerDown(e) },
{ target: window, eventName: "pointerup", action: (e: PointerEvent): void => onPointerUp(e) },
{ target: window, eventName: "mousedown", action: (e: MouseEvent): void => onMouseDown(e) },
{ target: window, eventName: "wheel", action: (e: WheelEvent): void => onMouseScroll(e), options: { passive: false } },
];
let viewportPointerInteractionOngoing = false;
@@ -60,7 +61,7 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
return true;
};
const onKeyDown = (e: KeyboardEvent) => {
const onKeyDown = (e: KeyboardEvent): void => {
const key = getLatinKey(e);
if (!key) return;
@@ -82,7 +83,7 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
}
};
const onKeyUp = (e: KeyboardEvent) => {
const onKeyUp = (e: KeyboardEvent): void => {
const key = getLatinKey(e);
if (!key) return;
@@ -95,14 +96,14 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
// Pointer events
const onPointerMove = (e: PointerEvent) => {
const onPointerMove = (e: PointerEvent): void => {
if (!e.buttons) viewportPointerInteractionOngoing = false;
const modifiers = makeModifiersBitfield(e);
editor.instance.on_mouse_move(e.clientX, e.clientY, e.buttons, modifiers);
};
const onPointerDown = (e: PointerEvent) => {
const onPointerDown = (e: PointerEvent): void => {
const { target } = e;
const inCanvas = target instanceof Element && target.closest(".canvas");
const inDialog = target instanceof Element && target.closest(".dialog-modal .floating-menu-content");
@@ -121,7 +122,7 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
}
};
const onPointerUp = (e: PointerEvent) => {
const onPointerUp = (e: PointerEvent): void => {
if (!e.buttons) viewportPointerInteractionOngoing = false;
const modifiers = makeModifiersBitfield(e);
@@ -130,13 +131,13 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
// Mouse events
const onMouseDown = (e: MouseEvent) => {
const onMouseDown = (e: MouseEvent): void => {
// Block middle mouse button auto-scroll mode (the circlar widget that appears and allows quick scrolling by moving the cursor above or below it)
// This has to be in `mousedown`, not `pointerdown`, to avoid blocking Vue's middle click detection on HTML elements
if (e.button === 1) e.preventDefault();
};
const onMouseScroll = (e: WheelEvent) => {
const onMouseScroll = (e: WheelEvent): void => {
const { target } = e;
const inCanvas = target instanceof Element && target.closest(".canvas");
@@ -155,7 +156,7 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
// Window events
const onWindowResize = (container: HTMLElement) => {
const onWindowResize = (container: HTMLElement): void => {
const viewports = Array.from(container.querySelectorAll(".canvas"));
const boundsOfViewports = viewports.map((canvas) => {
const bounds = canvas.getBoundingClientRect();
@@ -168,7 +169,7 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
if (boundsOfViewports.length > 0) editor.instance.bounds_of_viewports(data);
};
const onBeforeUnload = (e: BeforeUnloadEvent) => {
const onBeforeUnload = (e: BeforeUnloadEvent): void => {
const activeDocument = document.state.documents[document.state.activeDocumentIndex];
if (!activeDocument.is_saved) editor.instance.trigger_auto_save(activeDocument.id);
@@ -187,11 +188,11 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
// Event bindings
const addListeners = () => {
const addListeners = (): void => {
listeners.forEach(({ target, eventName, action, options }) => target.addEventListener(eventName, action, options));
};
const removeListeners = () => {
const removeListeners = (): void => {
listeners.forEach(({ target, eventName, action }) => target.removeEventListener(eventName, action));
};