mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 21:48:12 +08:00
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:
@@ -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));
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user