mirror of
https://github.com/stfrigerio/sqliteDB.git
synced 2026-07-22 05:38:02 +00:00
new plugin state to switch placeholders
This commit is contained in:
parent
bddc9800a8
commit
cbf9d4f81d
8 changed files with 271 additions and 124 deletions
56
main.ts
56
main.ts
|
|
@ -11,7 +11,7 @@ import { SQLiteDBSettingTab } from "./src/settingTab";
|
|||
import { inspectTableStructure, convertEntriesInNotes } from "./src/commands";
|
||||
import { processSqlBlock, processSqlChartBlock, DateNavigatorRenderer } from "./src/codeblocks";
|
||||
import { pickTableName } from "./src/helpers";
|
||||
import { SQLiteDBSettings, DEFAULT_SETTINGS } from "./src/types";
|
||||
import { replacePlaceholders } from "src/helpers/replacePlaceholders";
|
||||
|
||||
import { injectDatePickerStyles } from "src/styles/datePickerInject";
|
||||
import { injectDateNavigatorStyles, removeDateNavigatorStyles } from './src/styles/dateNavigationInject';
|
||||
|
|
@ -20,6 +20,9 @@ import { registerHabitCounter } from "./src/webcomponents/HabitCounter/registerH
|
|||
import { registerBooleanSwitch } from "src/webcomponents/BooleanSwitch/registerBooleanSwitch";
|
||||
import { registerTextInput } from "src/webcomponents/TextInput/registerTextInput";
|
||||
|
||||
import { SQLiteDBSettings, DEFAULT_SETTINGS } from "./src/types";
|
||||
import { pluginState } from "src/pluginState";
|
||||
|
||||
export default class SQLiteDBPlugin extends Plugin {
|
||||
settings: SQLiteDBSettings;
|
||||
private dbService: DBService;
|
||||
|
|
@ -30,6 +33,8 @@ export default class SQLiteDBPlugin extends Plugin {
|
|||
this.dbService = new DBService(this.app);
|
||||
await this.openDatabase();
|
||||
|
||||
pluginState.initialize(this.app);
|
||||
|
||||
injectDatePickerStyles();
|
||||
injectDateNavigatorStyles();
|
||||
|
||||
|
|
@ -40,6 +45,32 @@ export default class SQLiteDBPlugin extends Plugin {
|
|||
registerTextInput(el, this.app, this.dbService);
|
||||
});
|
||||
|
||||
//? Codeblocks
|
||||
this.registerMarkdownCodeBlockProcessor(
|
||||
"sql",
|
||||
async (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
|
||||
const processedSource = replacePlaceholders(source);
|
||||
await processSqlBlock(this.dbService, processedSource, el);
|
||||
}
|
||||
);
|
||||
|
||||
this.registerMarkdownCodeBlockProcessor(
|
||||
"sql-chart",
|
||||
async (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
|
||||
const processedSource = replacePlaceholders(source);
|
||||
await processSqlChartBlock(this.dbService, processedSource, el);
|
||||
}
|
||||
);
|
||||
|
||||
this.registerMarkdownCodeBlockProcessor(
|
||||
"date-header",
|
||||
(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
|
||||
const processedSource = replacePlaceholders(source);
|
||||
ctx.addChild(new DateNavigatorRenderer(el, this.app, processedSource));
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
//? Commands
|
||||
this.addCommand({
|
||||
id: "inspect-table-structure",
|
||||
|
|
@ -64,29 +95,6 @@ export default class SQLiteDBPlugin extends Plugin {
|
|||
},
|
||||
});
|
||||
|
||||
//? Codeblocks
|
||||
this.registerMarkdownCodeBlockProcessor(
|
||||
"sql",
|
||||
async (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
|
||||
await processSqlBlock(this.dbService, source, el);
|
||||
}
|
||||
);
|
||||
|
||||
this.registerMarkdownCodeBlockProcessor(
|
||||
"sql-chart",
|
||||
async (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
|
||||
await processSqlChartBlock(this.dbService, source, el);
|
||||
}
|
||||
);
|
||||
|
||||
this.registerMarkdownCodeBlockProcessor(
|
||||
"date-header",
|
||||
(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
|
||||
ctx.addChild(new DateNavigatorRenderer(el, this.app, source));
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
this.addSettingTab(new SQLiteDBSettingTab(this.app, this));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,60 +1,95 @@
|
|||
import { DBService } from "../../../DBService";
|
||||
import { SqlParams, ValidationError } from "../types";
|
||||
|
||||
export async function validateTable(db: any, params: SqlParams) {
|
||||
// Get table info
|
||||
const tableInfo = db.exec(`PRAGMA table_info("${params.table}")`);
|
||||
if (!tableInfo || tableInfo.length === 0) {
|
||||
export async function validateTable(dbService: DBService, params: SqlParams): Promise<ValidationError | null> {
|
||||
const tableName = params.table;
|
||||
let availableColumns: string[] = [];
|
||||
|
||||
try {
|
||||
// Fetch column info based on DB mode
|
||||
if (dbService.mode === "remote") {
|
||||
const result = await dbService.getQuery<{ name: string }>(
|
||||
`PRAGMA table_info("${tableName}")`
|
||||
);
|
||||
if (!result || result.length === 0) {
|
||||
// Handle case where table might exist but PRAGMA returns empty (less likely)
|
||||
// Or if getQuery returns null/empty for other reasons
|
||||
return {
|
||||
message: `Unable to fetch column info for table "${tableName}". It might not exist or there was an issue.`,
|
||||
availableColumns: []
|
||||
};
|
||||
}
|
||||
availableColumns = result.map((row: { name: string }) => row.name);
|
||||
} else {
|
||||
// Local mode
|
||||
const db = dbService.getDB();
|
||||
if (!db) {
|
||||
// Should ideally not happen if processSqlBlock checks first, but good practice
|
||||
return { message: "Database not loaded." };
|
||||
}
|
||||
const tableInfo = db.exec(`PRAGMA table_info("${tableName}")`);
|
||||
// Check if table exists based on PRAGMA result
|
||||
if (!tableInfo || tableInfo.length === 0 || !tableInfo[0].values || tableInfo[0].values.length === 0) {
|
||||
return {
|
||||
message: `Table "${tableName}" does not exist or is empty.`
|
||||
};
|
||||
}
|
||||
// Column name is typically the second item (index 1) in the PRAGMA result row
|
||||
availableColumns = tableInfo[0].values.map((row: any) => row[1] as string);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`[validateTable] Failed to fetch columns for table "${tableName}"`, error);
|
||||
// Return a generic error if PRAGMA fails
|
||||
return {
|
||||
message: `Table "${params.table}" does not exist.`
|
||||
message: `Error fetching column info for table "${tableName}".`,
|
||||
availableColumns: []
|
||||
};
|
||||
}
|
||||
|
||||
const availableColumns = tableInfo[0].values.map((row: any) => row[1] as string);
|
||||
|
||||
// Validate columns if specified
|
||||
if (params.columns) {
|
||||
const requestedColumns = params.columns.split(',').map(c => c.trim());
|
||||
for (const col of requestedColumns) {
|
||||
if (!availableColumns.includes(col)) {
|
||||
return {
|
||||
message: `Column "${col}" does not exist in table "${params.table}".`,
|
||||
availableColumns
|
||||
};
|
||||
}
|
||||
const invalidColumns = requestedColumns.filter(col => !availableColumns.includes(col));
|
||||
if (invalidColumns.length > 0) {
|
||||
return {
|
||||
message: `The following columns do not exist in table "${tableName}": ${invalidColumns.join(", ")}.`,
|
||||
availableColumns
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Validate dateColumn if specified
|
||||
if (params.dateColumn && !availableColumns.includes(params.dateColumn)) {
|
||||
return {
|
||||
message: `Date column "${params.dateColumn}" does not exist in table "${params.table}".`,
|
||||
message: `Date column "${params.dateColumn}" does not exist in table "${tableName}".`,
|
||||
availableColumns
|
||||
};
|
||||
}
|
||||
|
||||
// Validate filterColumn if specified
|
||||
if (params.filterColumn) {
|
||||
const filterColumns = Array.isArray(params.filterColumn)
|
||||
? params.filterColumn
|
||||
: [params.filterColumn];
|
||||
|
||||
for (const col of filterColumns) {
|
||||
if (!availableColumns.includes(col)) {
|
||||
return {
|
||||
message: `Filter column "${col}" does not exist in table "${params.table}".`,
|
||||
availableColumns
|
||||
};
|
||||
}
|
||||
const filterColumns = Array.isArray(params.filterColumn)
|
||||
? params.filterColumn
|
||||
: params.filterColumn.split(',').map(c => c.trim()); // Handle comma-separated string too
|
||||
|
||||
const invalidFilterCols = filterColumns.filter(col => !availableColumns.includes(col));
|
||||
if (invalidFilterCols.length > 0) {
|
||||
return {
|
||||
message: `The following filter columns do not exist in table "${tableName}": ${invalidFilterCols.join(", ")}.`,
|
||||
availableColumns
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Validate orderBy if specified
|
||||
if (params.orderBy && !availableColumns.includes(params.orderBy)) {
|
||||
return {
|
||||
message: `Order by column "${params.orderBy}" does not exist in table "${params.table}".`,
|
||||
message: `Order by column "${params.orderBy}" does not exist in table "${tableName}".`,
|
||||
availableColumns
|
||||
};
|
||||
}
|
||||
|
||||
// All checks passed
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1,40 +1,30 @@
|
|||
import { Notice } from "obsidian";
|
||||
import { DBService } from "../../DBService";
|
||||
import { parseSqlParams, validateTable, buildSqlQuery, renderResults } from "./helpers";
|
||||
import { pluginState } from "../../pluginState";
|
||||
|
||||
export async function processSqlBlock(dbService: DBService, source: string, el: HTMLElement) {
|
||||
const db = dbService.getDB();
|
||||
if (!db) {
|
||||
new Notice("No DB loaded. Please open the DB first.");
|
||||
el.createEl("p", { text: "Database not loaded." });
|
||||
return;
|
||||
}
|
||||
const params = parseSqlParams(source);
|
||||
|
||||
// replace @date with the selected date
|
||||
const injectedSource = source.replace(/@date/g, pluginState.selectedDate);
|
||||
const params = parseSqlParams(injectedSource);
|
||||
|
||||
if (!params) {
|
||||
el.createEl("p", { text: "Missing required parameter: table" });
|
||||
el.createEl("p", { text: "Example usage:" });
|
||||
el.createEl("pre", {
|
||||
el.createEl("pre", {
|
||||
text: `table: tasks
|
||||
columns: title, status
|
||||
filterColumn: status, priority
|
||||
filterValue: active, high
|
||||
dateColumn: dueDate
|
||||
startDate: 2024-01-01
|
||||
endDate: 2024-12-31
|
||||
limit: 10
|
||||
orderBy: dueDate
|
||||
orderDirection: asc`
|
||||
columns: title, status
|
||||
filterColumn: status, priority
|
||||
filterValue: active, high
|
||||
dateColumn: dueDate
|
||||
startDate: 2024-01-01
|
||||
endDate: 2024-12-31
|
||||
limit: 10
|
||||
orderBy: dueDate
|
||||
orderDirection: asc`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const validationError = await validateTable(db, params);
|
||||
const validationError = await validateTable(dbService, params);
|
||||
if (validationError) {
|
||||
el.createEl("p", { text: validationError.message });
|
||||
if (validationError.availableColumns) {
|
||||
|
|
@ -44,41 +34,59 @@ export async function processSqlBlock(dbService: DBService, source: string, el:
|
|||
}
|
||||
|
||||
const { query, queryParams } = buildSqlQuery(params);
|
||||
const result = db.exec(query, queryParams);
|
||||
|
||||
if (!result || result.length === 0 || result[0].values.length === 0) {
|
||||
el.createEl("p", {
|
||||
text: `No rows found in table "${params.table}"`,
|
||||
});
|
||||
|
||||
// Show the applied filters to help debug
|
||||
if (params.keyColumn && params.value) {
|
||||
el.createEl("p", { text: `Filter: ${params.keyColumn} = ${params.value}` });
|
||||
let resultsToRender: { columns: string[], values: any[][] } | null = null;
|
||||
|
||||
// Handle remote vs local DB
|
||||
if (dbService.mode === "remote") {
|
||||
const rows = await dbService.getQuery(query, queryParams);
|
||||
if (!rows || rows.length === 0) {
|
||||
// Keep existing no-rows message logic
|
||||
} else {
|
||||
// Convert remote result (array of objects) to the format renderResults expects
|
||||
const columns = Object.keys(rows[0]);
|
||||
const values = rows.map(obj => columns.map(col => obj[col]));
|
||||
resultsToRender = { columns, values };
|
||||
}
|
||||
if (params.dateColumn && (params.startDate || params.endDate)) {
|
||||
el.createEl("p", {
|
||||
text: `Date range: ${params.dateColumn} from ${params.startDate || 'start'} to ${params.endDate || 'end'}`
|
||||
});
|
||||
} else {
|
||||
// Local mode logic
|
||||
const db = dbService.getDB();
|
||||
if (!db) {
|
||||
new Notice("No DB loaded. Please open the DB first.");
|
||||
el.createEl("p", { text: "Database not loaded." });
|
||||
return; // Exit renderSqlBlock early
|
||||
}
|
||||
if (params.filterColumn && params.filterValue) {
|
||||
el.createEl("p", { text: `Additional filter: ${params.filterColumn} = ${params.filterValue}` });
|
||||
const result = db.exec(query, queryParams);
|
||||
if (!result || result.length === 0 || result[0].values.length === 0) {
|
||||
// Keep existing no-rows message logic
|
||||
} else {
|
||||
resultsToRender = result[0];
|
||||
}
|
||||
|
||||
el.createEl("p", {
|
||||
text: "Try adjusting your filters or date range.",
|
||||
});
|
||||
}
|
||||
|
||||
//? Check if we have results to render
|
||||
if (!resultsToRender) {
|
||||
el.createEl("p", { text: `No rows found matching the criteria.` });
|
||||
// Optionally display the specific criteria used from 'params' object
|
||||
let criteriaMsg = `Table: "${params.table}"`;
|
||||
if(params.startDate || params.endDate) criteriaMsg += ` | Dates: ${params.startDate} to ${params.endDate}`;
|
||||
if(params.filterColumn) criteriaMsg += ` | Filter: ${params.filterColumn}=${params.filterValue}`;
|
||||
el.createEl("p", { text: `Criteria: ${criteriaMsg}`, cls: 'sql-block-criteria-summary'}); // Add a class for styling
|
||||
return;
|
||||
}
|
||||
|
||||
renderResults(result[0], el);
|
||||
// Render results if found
|
||||
renderResults(resultsToRender, el);
|
||||
|
||||
} catch (error) {
|
||||
el.createEl("p", { text: "An error occurred while processing the SQL block." });
|
||||
el.empty(); // Clear before showing error
|
||||
el.createEl("p", { text: "An error occurred processing the SQL block." });
|
||||
el.createEl("p", { text: String(error) });
|
||||
// Optionally show the query that caused the error in development
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const { query, queryParams } = buildSqlQuery(params);
|
||||
console.error("SQL Block Error:", error);
|
||||
// Optionally show query info on error
|
||||
try {
|
||||
const { query, queryParams } = buildSqlQuery(params); // Try building query again for error display
|
||||
el.createEl("pre", { text: `Query: ${query}\nParams: ${JSON.stringify(queryParams)}` });
|
||||
}
|
||||
} catch (buildError) { /* Ignore if query build fails */ }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,13 @@
|
|||
import { DBService } from "../../DBService";
|
||||
import { parseChartParams, validateColumns, buildSqlQuery, processChartData } from "./helpers";
|
||||
import { ChartType, createChart } from "./charts";
|
||||
import { pluginState, DATE_CHANGED_EVENT_NAME } from "../../pluginState";
|
||||
import { Notice } from "obsidian";
|
||||
|
||||
export async function processSqlChartBlock(dbService: DBService, source: string, el: HTMLElement) {
|
||||
await renderChartBlock();
|
||||
|
||||
const onDateChange = async (e: CustomEvent) => {
|
||||
el.empty(); // Clear existing chart or messages
|
||||
await renderChartBlock();
|
||||
};
|
||||
|
||||
document.addEventListener(DATE_CHANGED_EVENT_NAME, onDateChange as EventListener);
|
||||
|
||||
async function renderChartBlock() {
|
||||
// replace @date with the selected date
|
||||
const injectedSource = source.replace(/@date/g, pluginState.selectedDate);
|
||||
const config = parseChartParams(injectedSource);
|
||||
const config = parseChartParams(source);
|
||||
|
||||
if (!config) {
|
||||
// @ts-ignore
|
||||
|
|
|
|||
|
|
@ -133,4 +133,52 @@ export function formatPeriodForDisplay(isoDate: string, period: NavigationPeriod
|
|||
default:
|
||||
return formatDateForDisplay(dateObj);
|
||||
}
|
||||
}
|
||||
|
||||
/** Calculates the ISO Week string (YYYY-Www). */
|
||||
export function getIsoWeekId(date: Date): string {
|
||||
const year = date.getUTCFullYear();
|
||||
const month = date.getUTCMonth();
|
||||
const weekNum = getISOWeekNumber(date);
|
||||
|
||||
if (weekNum === 1 && month === 11) return `${year + 1}-W01`;
|
||||
if (weekNum >= 52 && month === 0) return `${year - 1}-W${String(weekNum).padStart(2, '0')}`;
|
||||
return `${year}-W${String(weekNum).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/** Calculates the Quarter string (YYYY-Qq). */
|
||||
export function getQuarterId(date: Date): string {
|
||||
const year = date.getUTCFullYear();
|
||||
const quarter = Math.floor(date.getUTCMonth() / 3) + 1;
|
||||
return `${year}-Q${quarter}`;
|
||||
}
|
||||
|
||||
/** Calculates the Month string (YYYY-MM). */
|
||||
export function getMonthId(date: Date): string {
|
||||
const year = date.getUTCFullYear();
|
||||
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
||||
return `${year}-${month}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the specific period identifier string based on the date and period type.
|
||||
* e.g., '2024-W15', '2024-04', '2024-Q2', '2024', '2024-04-15'
|
||||
*/
|
||||
export function getPeriodId(isoDate: string, period: NavigationPeriod): string {
|
||||
const dateObj = parseDateISO(isoDate);
|
||||
if (!dateObj) {
|
||||
console.warn(`[getPeriodId] Invalid date: ${isoDate}, returning date string.`);
|
||||
return isoDate; // Fallback
|
||||
}
|
||||
|
||||
switch (period) {
|
||||
case 'day': return isoDate; // Returns 'YYYY-MM-DD'
|
||||
case 'week': return getIsoWeekId(dateObj); // Returns 'YYYY-Www'
|
||||
case 'month': return getMonthId(dateObj); // Returns 'YYYY-MM'
|
||||
case 'quarter': return getQuarterId(dateObj); // Returns 'YYYY-Qq'
|
||||
case 'year': return String(dateObj.getUTCFullYear()); // Returns 'YYYY'
|
||||
default:
|
||||
console.warn(`[getPeriodId] Unknown period type: ${period}, returning ISO date.`);
|
||||
return isoDate; // Fallback
|
||||
}
|
||||
}
|
||||
40
src/helpers/replacePlaceholders.ts
Normal file
40
src/helpers/replacePlaceholders.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { pluginState } from "src/pluginState";
|
||||
import { getPeriodId } from "./datePeriodUtils";
|
||||
import { parseDateISO, getISOWeekNumber } from "./dateUtils";
|
||||
|
||||
//? Helper function to replace all date/period placeholders
|
||||
export const replacePlaceholders = (source: string): string => {
|
||||
let processedSource = source;
|
||||
const selectedDateStr = pluginState.selectedDate;
|
||||
const selectedPeriod = pluginState.currentPeriod; // The *type* ('day', 'week'...)
|
||||
const startDate = pluginState.periodStartDate; // 'YYYY-MM-DD'
|
||||
const endDate = pluginState.periodEndDate; // 'YYYY-MM-DD'
|
||||
|
||||
//^ Calculate the specific Period ID String ('YYYY-Www', 'YYYY-MM', etc.)
|
||||
const periodId = getPeriodId(selectedDateStr, selectedPeriod);
|
||||
|
||||
const selectedDateObj = parseDateISO(selectedDateStr);
|
||||
|
||||
// --- Perform Replacements ---
|
||||
processedSource = processedSource.replace(/@startDate/g, startDate);
|
||||
processedSource = processedSource.replace(/@endDate/g, endDate);
|
||||
//? Replace @periodId with the calculated identifier string
|
||||
processedSource = processedSource.replace(/@periodId/g, periodId);
|
||||
//? Keep @date for the specific selected day
|
||||
processedSource = processedSource.replace(/@date/g, selectedDateStr);
|
||||
|
||||
//? Replace individual components if needed
|
||||
if (selectedDateObj) {
|
||||
processedSource = processedSource.replace(/@year/g, String(selectedDateObj.getUTCFullYear()));
|
||||
processedSource = processedSource.replace(/@quarter/g, String(Math.floor(selectedDateObj.getUTCMonth() / 3) + 1));
|
||||
processedSource = processedSource.replace(/@month/g, String(selectedDateObj.getUTCMonth() + 1).padStart(2, '0'));
|
||||
// Use the week part from the period ID for consistency
|
||||
processedSource = processedSource.replace(/@week/g, periodId.startsWith(selectedDateObj.getUTCFullYear() + "-W") ? periodId.split('-')[1] : 'W' + String(getISOWeekNumber(selectedDateObj)).padStart(2,'0')); // Extracts Www
|
||||
processedSource = processedSource.replace(/@day/g, String(selectedDateObj.getUTCDate()).padStart(2, '0'));
|
||||
} else {
|
||||
// Clear date component placeholders if date is invalid
|
||||
processedSource = processedSource.replace(/@year|@quarter|@month|@week|@day/g, '');
|
||||
}
|
||||
|
||||
return processedSource;
|
||||
};
|
||||
|
|
@ -1,18 +1,27 @@
|
|||
import { App, MarkdownView } from "obsidian";
|
||||
import { NavigationPeriod } from "./codeblocks/dateHeader/dateNavigator.types";
|
||||
import { calculatePeriodRange } from "./helpers/datePeriodUtils";
|
||||
|
||||
export const DATE_CHANGED_EVENT_NAME = 'plugin-date-changed';
|
||||
|
||||
export class PluginState {
|
||||
private app: App | null = null;
|
||||
|
||||
private _selectedDate: string; // YYYY-MM-DD (Represents a specific day within the current period)
|
||||
private _currentPeriod: NavigationPeriod;
|
||||
private _periodStartDate: string; // YYYY-MM-DD
|
||||
private _periodEndDate: string; // YYYY-MM-DD
|
||||
|
||||
constructor(initialDate?: string, initialPeriod: NavigationPeriod = 'day') {
|
||||
// Initialize state, calculating initial range
|
||||
public initialize(app: App, initialDate?: string, initialPeriod: NavigationPeriod = 'day'): void {
|
||||
this.app = app;
|
||||
this._currentPeriod = initialPeriod;
|
||||
this.selectedDate = initialDate ?? new Date().toISOString().split("T")[0]; // Trigger setter logic
|
||||
// Call setter to perform initial calculation and dispatch (if needed)
|
||||
this.selectedDate = initialDate ?? new Date().toISOString().split("T")[0];
|
||||
// Ensure initial range is set if date wasn't changed by setter
|
||||
if (!this._periodStartDate) {
|
||||
this._recalculatePeriodRange();
|
||||
}
|
||||
console.log("[PluginState] Initialized.");
|
||||
}
|
||||
|
||||
// --- Getters ---
|
||||
|
|
@ -34,7 +43,7 @@ export class PluginState {
|
|||
if (this._selectedDate !== newIsoDate) {
|
||||
this._selectedDate = newIsoDate;
|
||||
this._recalculatePeriodRange(); // Recalculate start/end based on new selected date and current period
|
||||
this._dispatchDateChangeEvent();
|
||||
this._dispatchDateChangeEventAndRefreshViews();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -43,12 +52,10 @@ export class PluginState {
|
|||
if (this._currentPeriod !== newPeriod) {
|
||||
this._currentPeriod = newPeriod;
|
||||
this._recalculatePeriodRange(); // Recalculate start/end based on the new period and current selected date
|
||||
this._dispatchDateChangeEvent(); // Also notify listeners of period change implicitly via date change event
|
||||
this._dispatchDateChangeEventAndRefreshViews(); // Also notify listeners of period change implicitly via date change event
|
||||
}
|
||||
}
|
||||
|
||||
// --- Internal Methods ---
|
||||
|
||||
/** Recalculates start and end dates based on current _selectedDate and _currentPeriod */
|
||||
private _recalculatePeriodRange(): void {
|
||||
const range = calculatePeriodRange(this._selectedDate, this._currentPeriod);
|
||||
|
|
@ -65,15 +72,26 @@ export class PluginState {
|
|||
}
|
||||
|
||||
/** Dispatches the custom event to notify listeners of changes */
|
||||
private _dispatchDateChangeEvent(): void {
|
||||
private _dispatchDateChangeEventAndRefreshViews(): void {
|
||||
document.dispatchEvent(new CustomEvent(DATE_CHANGED_EVENT_NAME, {
|
||||
detail: {
|
||||
newIsoDate: this._selectedDate, // The specific selected day
|
||||
newPeriod: this._currentPeriod, // The current period type
|
||||
newStartDate: this._periodStartDate, // Start of the period
|
||||
newEndDate: this._periodEndDate // End of the period
|
||||
}
|
||||
detail: { /* ... date/period details ... */ }
|
||||
}));
|
||||
|
||||
if (this.app) {
|
||||
this.app.workspace.getLeavesOfType('markdown').forEach(leaf => {
|
||||
if (leaf.view instanceof MarkdownView) {
|
||||
const view = leaf.view;
|
||||
//? Force re-render of the preview mode.
|
||||
//? This should cause code blocks to be re-processed.
|
||||
view.previewMode?.rerender(true);
|
||||
//? For Live Preview, forcing re-render is more complex and might
|
||||
//? require interacting with the CodeMirror view state if the simple
|
||||
//? previewMode rerender isn't sufficient. Start with this.
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn("[PluginState] Cannot refresh views: App instance not available.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export const registerHabitCounter = (el: HTMLElement, dbService: DBService) => {
|
|||
placeholders.forEach((placeholderEl) => { //~ Removed index as it wasn't used
|
||||
if (!(placeholderEl instanceof HTMLElement)) return;
|
||||
|
||||
const habitKey = placeholderEl.dataset.habit; //^ Read 'habit' data attribute
|
||||
const habitKey = placeholderEl.dataset.habit;
|
||||
const date = placeholderEl.dataset.date;
|
||||
const emoji = placeholderEl.dataset.emoji;
|
||||
const table = placeholderEl.dataset.table;
|
||||
|
|
|
|||
Loading…
Reference in a new issue