feat(views): add config override support for Bases integration

- Add setConfigOverride method to QuadrantComponent
- Add setConfigOverride method to ForecastComponent
- Add config override support to CalendarComponent and views
- Pass effective config to MonthView, WeekView, and YearView
- Minor formatting improvements in KanbanComponent
- Register quadrant as new Bases view type in registerBasesViews

This enables Bases plugin to override view-specific configurations
(firstDayOfWeek, hideWeekends, urgentTag, etc.) on a per-view basis,
allowing users to customize view behavior independently in different
Bases views while maintaining default plugin settings.
This commit is contained in:
Quorafind 2025-10-09 11:19:54 +08:00
parent 0cba1f3e67
commit 227f85d793
8 changed files with 156 additions and 54 deletions

View file

@ -64,6 +64,10 @@ export class CalendarComponent extends Component {
private badgeEventsCache: Map<string, CalendarEvent[]> = new Map();
private badgeEventsCacheVersion: number = 0;
// Per-view override from Bases (firstDayOfWeek, hideWeekends, ...)
private configOverride: Partial<import("@/common/setting-definition").CalendarSpecificConfig> | null = null;
constructor(
app: App,
plugin: TaskProgressBarPlugin,
@ -92,6 +96,7 @@ export class CalendarComponent extends Component {
this.currentViewMode = viewMode as CalendarViewMode;
}
console.log("CalendarComponent initialized with params:", this.params);
}
@ -107,6 +112,8 @@ export class CalendarComponent extends Component {
override onunload() {
super.onunload();
// Detach the active view component if it exists
if (this.activeViewComponent) {
this.removeChild(this.activeViewComponent);
this.activeViewComponent = null;
@ -132,6 +139,8 @@ export class CalendarComponent extends Component {
this.invalidateBadgeEventsCache();
this.processTasks();
// Only update the currently active view
if (this.activeViewComponent) {
this.activeViewComponent.updateEvents(this.events);
} else {
@ -153,6 +162,8 @@ export class CalendarComponent extends Component {
this.app.saveLocalStorage(
"task-genius:calendar-view",
this.currentViewMode
);
}
}
@ -169,6 +180,7 @@ export class CalendarComponent extends Component {
this.currentDate.add(1, unit);
}
this.render(); // Re-render header and update the view
}
/**
@ -304,6 +316,7 @@ export class CalendarComponent extends Component {
);
switch (this.currentViewMode) {
case "month":
const effMonth = this.getEffectiveCalendarConfig();
nextViewComponent = new MonthView(
this.app,
this.plugin,
@ -320,10 +333,12 @@ export class CalendarComponent extends Component {
onEventComplete: this.onEventComplete,
getBadgeEventsForDate:
this.getBadgeEventsForDate.bind(this),
}
},
effMonth
);
break;
case "week":
const effWeek = this.getEffectiveCalendarConfig();
nextViewComponent = new WeekView(
this.app,
this.plugin,
@ -340,7 +355,8 @@ export class CalendarComponent extends Component {
onEventComplete: this.onEventComplete,
getBadgeEventsForDate:
this.getBadgeEventsForDate.bind(this),
}
},
effWeek
);
break;
case "day":
@ -374,6 +390,7 @@ export class CalendarComponent extends Component {
);
break;
case "year":
const effYear = this.getEffectiveCalendarConfig();
nextViewComponent = new YearView(
this.app,
this.plugin,
@ -387,7 +404,8 @@ export class CalendarComponent extends Component {
onDayHover: this.onDayHover,
onMonthClick: this.onMonthClick,
onMonthHover: this.onMonthHover,
}
},
effYear
);
break;
default:
@ -690,11 +708,9 @@ export class CalendarComponent extends Component {
const startOfMonth = this.currentDate.clone().startOf("month");
const endOfMonth = this.currentDate.clone().endOf("month");
// Get first day of week setting
const viewConfig = this.plugin.settings.viewConfiguration.find(
(v) => v.id === this.viewId
)?.specificConfig as any; // Use any for now to avoid import complexity
const firstDayOfWeek = viewConfig?.firstDayOfWeek ?? 0;
// Get first day of week setting (effective with override)
const effCfg = this.getEffectiveCalendarConfig();
const firstDayOfWeek = effCfg.firstDayOfWeek ?? 0;
const gridStart = startOfMonth
.clone()
@ -917,6 +933,21 @@ export class CalendarComponent extends Component {
public onEventComplete = (ev: MouseEvent, event: CalendarEvent) => {
this.params?.onTaskCompleted?.(event);
};
// Allow external overrides (e.g., from Bases) and compute effective config
public setConfigOverride(
override: Partial<import("@/common/setting-definition").CalendarSpecificConfig> | null
): void {
this.configOverride = override ?? null;
// Re-render to apply new config
this.render();
}
private getEffectiveCalendarConfig(): Partial<import("@/common/setting-definition").CalendarSpecificConfig> {
const baseCfg = this.plugin.settings.viewConfiguration.find((v) => v.id === this.viewId)?.specificConfig as Partial<import("@/common/setting-definition").CalendarSpecificConfig> | undefined;
return { ...(baseCfg ?? {}), ...(this.configOverride ?? {}) };
}
}
// Helper function (example - might move to a utils file)

View file

@ -29,6 +29,7 @@ export class MonthView extends CalendarViewComponent {
private app: App; // Keep app reference if needed directly
private plugin: TaskProgressBarPlugin; // Keep plugin reference if needed directly
private sortableInstances: Sortable[] = []; // Store sortable instances for cleanup
private overrideConfig?: Partial<CalendarSpecificConfig>;
constructor(
app: App,
@ -37,21 +38,23 @@ export class MonthView extends CalendarViewComponent {
private currentViewId: string,
currentDate: moment.Moment,
events: CalendarEvent[],
options: CalendarViewOptions // Use the base options type
options: CalendarViewOptions, // Use the base options type
overrideConfig?: Partial<CalendarSpecificConfig>
) {
super(plugin, app, containerEl, events, options); // Call base constructor
this.app = app; // Still store app if needed directly
this.plugin = plugin; // Still store plugin if needed directly
this.currentDate = currentDate;
this.overrideConfig = overrideConfig;
}
render(): void {
// Get view settings, including the first day of the week override and weekend hiding
// Get view settings, prefer override values when provided
const viewConfig = this.plugin.settings.viewConfiguration.find(
(v) => v.id === this.currentViewId
)?.specificConfig as CalendarSpecificConfig; // Assuming 'calendar' view for settings lookup, adjust if needed
const firstDayOfWeekSetting = viewConfig?.firstDayOfWeek;
const hideWeekends = viewConfig?.hideWeekends ?? false;
)?.specificConfig as CalendarSpecificConfig;
const firstDayOfWeekSetting = this.overrideConfig?.firstDayOfWeek ?? viewConfig?.firstDayOfWeek;
const hideWeekends = (this.overrideConfig?.hideWeekends ?? viewConfig?.hideWeekends) ?? false;
// Default to Sunday (0) if the setting is undefined, following 0=Sun, 1=Mon, ..., 6=Sat
const effectiveFirstDay =
firstDayOfWeekSetting === undefined ? 0 : firstDayOfWeekSetting;

View file

@ -20,6 +20,8 @@ export class WeekView extends CalendarViewComponent {
private app: App; // Keep app reference
private plugin: TaskProgressBarPlugin; // Keep plugin reference
private sortableInstances: Sortable[] = []; // Store sortable instances for cleanup
private overrideConfig?: Partial<CalendarSpecificConfig>;
// Removed onEventClick/onMouseHover properties, now in this.options
constructor(
@ -29,25 +31,25 @@ export class WeekView extends CalendarViewComponent {
private currentViewId: string,
currentDate: moment.Moment,
events: CalendarEvent[],
options: CalendarViewOptions // Use the base options type
options: CalendarViewOptions, // Use the base options type
overrideConfig?: Partial<CalendarSpecificConfig>
) {
super(plugin, app, containerEl, events, options); // Call base constructor
this.app = app; // Store app
this.plugin = plugin; // Store plugin
this.currentDate = currentDate;
this.overrideConfig = overrideConfig;
}
render(): void {
// Get view settings, including the first day of the week override and weekend hiding
// Get view settings, prefer override values when provided
const viewConfig = getViewSettingOrDefault(
this.plugin,
this.currentViewId
);
console.log("viewConfig calendar", viewConfig);
const firstDayOfWeekSetting = (
viewConfig.specificConfig as CalendarSpecificConfig
).firstDayOfWeek;
const hideWeekends = (viewConfig.specificConfig as CalendarSpecificConfig)?.hideWeekends ?? false;
const firstDayOfWeekSetting = (this.overrideConfig?.firstDayOfWeek ?? (viewConfig.specificConfig as CalendarSpecificConfig).firstDayOfWeek);
const hideWeekends = (this.overrideConfig?.hideWeekends ?? (viewConfig.specificConfig as CalendarSpecificConfig)?.hideWeekends) ?? false;
// Default to Sunday (0) if the setting is undefined, following 0=Sun, 1=Mon, ..., 6=Sat
const effectiveFirstDay =
firstDayOfWeekSetting === undefined ? 0 : firstDayOfWeekSetting;
@ -369,7 +371,7 @@ export class WeekView extends CalendarViewComponent {
taskId: calendarEvent.id,
updates: updatedTask,
});
if (!result.success) {
throw new Error(`Failed to update task: ${result.error}`);
}

View file

@ -18,6 +18,8 @@ export class YearView extends CalendarViewComponent {
private app: App; // Keep app reference
private plugin: TaskProgressBarPlugin; // Keep plugin reference
// Removed specific click/hover properties, use this.options
private overrideConfig?: Partial<CalendarSpecificConfig>;
constructor(
app: App,
@ -25,12 +27,14 @@ export class YearView extends CalendarViewComponent {
containerEl: HTMLElement,
currentDate: moment.Moment,
events: CalendarEvent[],
options: CalendarViewOptions // Use base options type
options: CalendarViewOptions, // Use base options type
overrideConfig?: Partial<CalendarSpecificConfig>
) {
super(plugin, app, containerEl, events, options); // Call base constructor
this.app = app;
this.plugin = plugin;
this.currentDate = currentDate;
this.overrideConfig = overrideConfig;
}
render(): void {
@ -64,12 +68,10 @@ export class YearView extends CalendarViewComponent {
} events for year ${year} in ${endTimeFilter - startTimeFilter}ms`
); // Log filtering time
// Get view settings (assuming 'calendar' or a 'year' specific setting)
// Get view settings (prefer override when provided)
const viewConfig = getViewSettingOrDefault(this.plugin, "calendar"); // Adjust if needed
const firstDayOfWeekSetting = (
viewConfig.specificConfig as CalendarSpecificConfig
).firstDayOfWeek;
const hideWeekends = (viewConfig.specificConfig as CalendarSpecificConfig)?.hideWeekends ?? false;
const firstDayOfWeekSetting = (this.overrideConfig?.firstDayOfWeek ?? (viewConfig.specificConfig as CalendarSpecificConfig).firstDayOfWeek);
const hideWeekends = (this.overrideConfig?.hideWeekends ?? (viewConfig.specificConfig as CalendarSpecificConfig)?.hideWeekends) ?? false;
// Add hide-weekends class if weekend hiding is enabled
if (hideWeekends) {

View file

@ -55,7 +55,7 @@ export class KanbanComponent extends Component {
private columnSortableInstance: Sortable | null = null;
private tasks: Task[] = [];
private allTasks: Task[] = [];
private currentViewId: string = "kanban"; // 新增当前视图ID
private currentViewId = "kanban"; // 新增当前视图ID
private columnOrder: string[] = [];
private params: {
onTaskStatusUpdate?: (
@ -74,7 +74,7 @@ export class KanbanComponent extends Component {
order: "desc",
label: "Priority (High to Low)",
};
private hideEmptyColumns: boolean = false;
private hideEmptyColumns = false;
private configOverride: Partial<KanbanSpecificConfig> | null = null; // Configuration override from Bases view
constructor(
@ -91,7 +91,7 @@ export class KanbanComponent extends Component {
onTaskCompleted?: (task: Task) => void;
onTaskContextMenu?: (ev: MouseEvent, task: Task) => void;
} = {},
viewId: string = "kanban" // 新增视图ID参数
viewId = "kanban" // 新增视图ID参数
) {
super();
this.app = app;
@ -534,7 +534,7 @@ export class KanbanComponent extends Component {
// Resolve effective config (Bases override wins over plugin settings)
const kanbanConfig = this.getEffectiveKanbanConfig();
console.log('[Kanban] renderColumns effective config', kanbanConfig);
console.log('[Kanban] renderColumns effective config', kanbanConfig);
const groupBy = kanbanConfig?.groupBy || "status";

View file

@ -7,6 +7,7 @@ import "@/styles/quadrant/quadrant.css";
import { t } from "@/translations/helper";
import { FilterComponent } from "@/components/features/task/filter/in-view/filter";
import { ActiveFilter } from "@/components/features/task/filter/in-view/filter-type";
import { QuadrantSpecificConfig } from "@/common/setting-definition";
export interface QuadrantSortOption {
field:
@ -95,6 +96,10 @@ export class QuadrantComponent extends Component {
};
private hideEmptyColumns: boolean = false;
// Per-view override from Bases
private configOverride: Partial<QuadrantSpecificConfig> | null = null;
// Quadrant-specific configuration
private get quadrantConfig() {
const view = this.plugin.settings.viewConfiguration.find(
@ -105,19 +110,18 @@ export class QuadrantComponent extends Component {
view.specificConfig &&
view.specificConfig.viewType === "quadrant"
) {
return view.specificConfig as any;
return { ...(view.specificConfig as any), ...(this.configOverride ?? {}) };
}
// Fallback to default quadrant config
const defaultView = this.plugin.settings.viewConfiguration.find(
(v) => v.id === "quadrant"
);
return (
(defaultView?.specificConfig as any) || {
urgentTag: "#urgent",
importantTag: "#important",
urgentThresholdDays: 3,
}
);
const base = (defaultView?.specificConfig as any) || {
urgentTag: "#urgent",
importantTag: "#important",
urgentThresholdDays: 3,
};
return { ...base, ...(this.configOverride ?? {}) };
}
constructor(
@ -144,6 +148,8 @@ export class QuadrantComponent extends Component {
this.containerEl = parentEl.createDiv(
"tg-quadrant-component-container"
);
this.tasks = initialTasks;
this.params = params;
}
@ -153,6 +159,14 @@ export class QuadrantComponent extends Component {
this.render();
}
public setConfigOverride(override: Partial<QuadrantSpecificConfig> | null): void {
this.configOverride = override ?? null;
// Re-render to apply new config safely
this.cleanup();
this.render();
}
override onunload() {
this.cleanup();
super.onunload();

View file

@ -60,6 +60,10 @@ export class ForecastComponent extends Component {
private treeComponents: TaskTreeItemComponent[] = [];
private allTasksMap: Map<string, Task> = new Map();
// Per-view override from Bases
private configOverride: Partial<ForecastSpecificConfig> | null = null;
constructor(
private parentEl: HTMLElement,
private app: App,
@ -77,6 +81,8 @@ export class ForecastComponent extends Component {
super();
// Initialize dates
this.currentDate = new Date();
this.currentDate.setHours(0, 0, 0, 0);
this.selectedDate = new Date(this.currentDate);
}
@ -155,6 +161,50 @@ export class ForecastComponent extends Component {
this.registerDomEvent(window, "focus", this.windowFocusHandler);
}
public setConfigOverride(override: Partial<ForecastSpecificConfig> | null): void {
this.configOverride = override ?? null;
this.rebuildCalendarWithEffectiveOptions();
}
private getEffectiveForecastConfig(): Partial<ForecastSpecificConfig> {
const baseCfg = this.plugin.settings.viewConfiguration.find((v) => v.id === "forecast")?.specificConfig as ForecastSpecificConfig | undefined;
return { ...(baseCfg ?? {}), ...(this.configOverride ?? {}) };
}
private rebuildCalendarWithEffectiveOptions(): void {
if (!this.calendarContainerEl) return;
// Remove old calendar component if exists
if (this.calendarComponent) {
this.removeChild(this.calendarComponent);
}
this.calendarContainerEl.empty();
const eff = this.getEffectiveForecastConfig();
const calendarOptions: Partial<CalendarOptions> = {
firstDayOfWeek: eff.firstDayOfWeek ?? 0,
showWeekends: !(eff.hideWeekends ?? false),
showTaskCounts: true,
};
this.calendarComponent = new CalendarComponent(this.calendarContainerEl, calendarOptions);
this.addChild(this.calendarComponent);
this.calendarComponent.load();
// Restore state and tasks
this.calendarComponent.setCurrentDate(this.currentDate);
this.calendarComponent.selectDate(this.selectedDate);
this.calendarComponent.setTasks(this.allTasks);
// Rebind selection handler
this.calendarComponent.onDateSelected = (date, tasks) => {
const selectedDate = new Date(date);
selectedDate.setHours(0, 0, 0, 0);
this.selectedDate = selectedDate;
this.updateDueSoonSection();
this.refreshDateSectionsUI();
if (Platform.isPhone) {
this.toggleLeftColumnVisibility(false);
}
};
}
private createForecastHeader() {
this.forecastHeaderEl = this.taskContainerEl.createDiv({
cls: "forecast-header",
@ -293,22 +343,15 @@ export class ForecastComponent extends Component {
cls: "forecast-calendar-section",
});
// Create and initialize calendar component
const forecastConfig = this.plugin.settings.viewConfiguration.find(
(view) => view.id === "forecast"
)?.specificConfig as ForecastSpecificConfig;
// Convert ForecastSpecificConfig to CalendarOptions
// Create and initialize calendar component using effective config (Bases override + settings)
const eff = this.getEffectiveForecastConfig();
const calendarOptions: Partial<CalendarOptions> = {
firstDayOfWeek: forecastConfig?.firstDayOfWeek ?? 0,
showWeekends: !(forecastConfig?.hideWeekends ?? false), // Invert hideWeekends to showWeekends
firstDayOfWeek: eff.firstDayOfWeek ?? 0,
showWeekends: !(eff.hideWeekends ?? false), // Invert hideWeekends to showWeekends
showTaskCounts: true,
};
this.calendarComponent = new CalendarComponent(
this.calendarContainerEl,
calendarOptions
);
this.calendarComponent = new CalendarComponent(this.calendarContainerEl, calendarOptions);
this.addChild(this.calendarComponent);
this.calendarComponent.load();
@ -983,23 +1026,23 @@ export class ForecastComponent extends Component {
// 基于选择日期重新分类所有任务
const selectedTimestamp = new Date(this.selectedDate).setHours(0, 0, 0, 0);
// 获取有相关日期的任务
const tasksWithRelevantDate = this.allTasks.filter(
(task) => this.getRelevantDate(task) !== undefined
);
// 相对于选择日期重新分类
const pastTasksRelativeToSelected = tasksWithRelevantDate.filter((task) => {
const relevantTimestamp = this.getRelevantDate(task)!;
return relevantTimestamp < selectedTimestamp;
});
const selectedDateTasks = tasksWithRelevantDate.filter((task) => {
const relevantTimestamp = this.getRelevantDate(task)!;
return relevantTimestamp === selectedTimestamp;
});
const futureTasksRelativeToSelected = tasksWithRelevantDate.filter((task) => {
const relevantTimestamp = this.getRelevantDate(task)!;
return relevantTimestamp > selectedTimestamp;

View file

@ -88,6 +88,13 @@ const TASK_GENIUS_BASES_VIEWS: TaskGeniusBasesViewType[] = [
defaultViewMode: 'flagged',
description: 'View high-priority flagged tasks'
},
{
id: 'task-genius-quadrant',
name: 'Quadrant (Task Genius)',
icon: 'lucide-grid',
defaultViewMode: 'quadrant',
description: 'Organize tasks using the Eisenhower Matrix'
}
];
/**