stfrigerio_sqliteDB/src/webcomponents/HabitCounter/HabitCounter.ts
2025-04-05 14:01:01 +02:00

130 lines
No EOL
5.7 KiB
TypeScript

import { DBService } from "../../DBService";
import { HabitDataService } from "./services/HabitDataService";
import { applyHabitCounterStyles } from "./styles/applyHabitCounterStyles";
import { buildHabitCounterDOM } from "./dom/buildHabitCounterDOM";
import { createDecrementHandler, createIncrementHandler } from "./eventHandlers/clickHandlers";
import { loadHabitData } from "./data/loadHabitData";
import { updateHabitData } from "./data/updateHabitData";
import {
updateDisplayValue,
updateStaticUI,
setComponentErrorState,
clearComponentErrorState,
HabitCounterUIElements
} from "./dom/uiUpdaters";
//? Main Web Component class, acts as an orchestrator.
export class HabitCounter extends HTMLElement {
// --- State ---
public habitKey: string = ""; //? Make public if needed by handlers/helpers
public initialDate: string = "";
public emoji: string = "";
public table: string = "";
public currentValue: number = 0;
private _initialLoadTriggered: boolean = false;
private _isInitialized: boolean = false;
// --- Dependencies ---
public habitDataService: HabitDataService | null = null; //? Public for helpers
// --- Shadow DOM Element References ---
//? Store the object returned by the builder
private uiElements: HabitCounterUIElements = {
wrapper: null, valueDisplay: null, labelElement: null, minusButton: null, plusButton: null
};
// --- Lifecycle ---
connectedCallback() {
if (this._isInitialized) return;
//& console.log("[HabitCounter Component] connectedCallback - Initializing...");
//~ Create handlers bound to this instance *before* building DOM
const handlers = {
handleDecrement: createDecrementHandler(this),
handleIncrement: createIncrementHandler(this),
};
this.uiElements = buildHabitCounterDOM(this.attachShadow({ mode: "open" }), handlers); //~ Build DOM via helper
applyHabitCounterStyles(this.shadowRoot!); //~ Apply styles via helper
this._isInitialized = true;
//& console.log("[HabitCounter Component] Initialization complete.");
}
// --- Public Methods ---
public setDbService(service: DBService): void {
//& console.log("[HabitCounter Component] setDbService called.");
if (!service) {
console.error("[HabitCounter Component] Invalid DBService provided.");
this.showErrorState("Setup Error"); //~ Use helper
return;
}
try {
this.habitDataService = new HabitDataService(service); //~ Initialize service dependency
} catch (error) {
console.error("[HabitCounter Component] Failed to initialize HabitDataService:", error);
this.showErrorState("Setup Error");
return;
}
this._readAttributesAndInitLoad(); //~ Trigger attribute reading and loading
}
// --- Internal Orchestration ---
private _readAttributesAndInitLoad(): void {
//& console.log("[HabitCounter Component] _readAttributesAndInitLoad running.");
this.habitKey = this.getAttribute("habit") ?? "";
this.initialDate = this.getAttribute("date") ?? "@date";
this.emoji = this.getAttribute("emoji") ?? "❓";
this.table = this.getAttribute("table") ?? "";
// --- Defer UI update and load trigger slightly ---
requestAnimationFrame(() => {
//& console.log(`[HabitCounter Component ${this.habitKey}] Deferred execution starting.`);
//? Now access uiElements, which should be reliably assigned.
console.log(`[HabitCounter Component ${this.habitKey}] DEBUG: Inspecting this.uiElements within deferred call:`, this.uiElements);
updateStaticUI(this.uiElements, this.emoji, this.habitKey); //~ Update static parts of UI
if (!this.table) {
console.warn(`[HabitCounter Component ${this.habitKey}] Table attribute missing.`);
this.showErrorState("No table");
return;
}
if (!this._initialLoadTriggered) {
this._initialLoadTriggered = true;
//& console.log(`[HabitCounter Component ${this.habitKey}] Triggering initial loadHabitData...`);
loadHabitData(this).catch(err => console.error("Unhandled error during initial load:", err)); //~ Load data via helper
} else {
console.log(`[HabitCounter Component ${this.habitKey}] Initial load already triggered.`);
}
});
}
//? Public method called by click handlers via instance reference
public async _updateData(delta: number): Promise<void> {
//& console.log(`[HabitCounter Component ${this.habitKey}] _updateData called with delta: ${delta}`);
await updateHabitData(this, delta); //~ Update data via helper
}
//? Public method called by data helpers to update display
public _updateDisplay(newValue: number): void {
//& console.log(`[HabitCounter Component ${this.habitKey}] _updateDisplay called with value: ${newValue}`);
this.currentValue = newValue; //? Update internal state first
updateDisplayValue(this.uiElements, newValue); //~ Update DOM via helper
this.clearErrorState(); //? Clear errors on successful update
}
//? Public methods for UI state helpers
public showErrorState(message?: string): void {
setComponentErrorState(this.uiElements, message); //~ Set error via helper
}
public clearErrorState(): void {
clearComponentErrorState(this.uiElements); //~ Clear error via helper
}
}
// --- Custom Element Definition ---
if (!customElements.get("habit-counter")) {
customElements.define("habit-counter", HabitCounter);
}