mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
feat: support new progressbar
This commit is contained in:
parent
d5af8b94ff
commit
3b6e70f98d
6 changed files with 486 additions and 135 deletions
0
design/task-view.md
Normal file
0
design/task-view.md
Normal file
|
|
@ -3,13 +3,24 @@ import { TaskFilterOptions } from "../editor-ext/filterTasks";
|
|||
import { t } from "../translations/helper";
|
||||
|
||||
export interface TaskProgressBarSettings {
|
||||
showProgressBar: boolean;
|
||||
progressBarDisplayMode: "graphical" | "text" | "both" | "none";
|
||||
addTaskProgressBarToHeading: boolean;
|
||||
addProgressBarToNonTaskBullet: boolean;
|
||||
enableHeadingProgressBar: boolean;
|
||||
addNumberToProgressBar: boolean;
|
||||
showPercentage: boolean;
|
||||
|
||||
// Progress text display options
|
||||
displayMode?:
|
||||
| "percentage"
|
||||
| "bracketPercentage"
|
||||
| "fraction"
|
||||
| "bracketFraction"
|
||||
| "detailed"
|
||||
| "custom"
|
||||
| "range-based";
|
||||
customFormat?: string;
|
||||
|
||||
progressRanges: Array<{
|
||||
min: number;
|
||||
max: number;
|
||||
|
|
@ -110,7 +121,7 @@ export interface TaskProgressBarSettings {
|
|||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: TaskProgressBarSettings = {
|
||||
showProgressBar: false,
|
||||
progressBarDisplayMode: "both",
|
||||
addTaskProgressBarToHeading: false,
|
||||
addProgressBarToNonTaskBullet: false,
|
||||
enableHeadingProgressBar: false,
|
||||
|
|
@ -125,6 +136,10 @@ export const DEFAULT_SETTINGS: TaskProgressBarSettings = {
|
|||
hideProgressBarFolders: "",
|
||||
hideProgressBarMetadata: "hide-progress-bar",
|
||||
|
||||
// Progress text display options
|
||||
displayMode: "bracketFraction",
|
||||
customFormat: "[{{COMPLETED}}/{{TOTAL}}]",
|
||||
|
||||
// Default task statuses
|
||||
taskStatuses: {
|
||||
completed: "x|X",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,212 @@ export interface HTMLElementWithView extends HTMLElement {
|
|||
view: EditorView;
|
||||
}
|
||||
|
||||
export interface ProgressData {
|
||||
completed: number;
|
||||
total: number;
|
||||
inProgress?: number;
|
||||
abandoned?: number;
|
||||
notStarted?: number;
|
||||
planned?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the progress text according to settings and data
|
||||
* Supports various display modes including fraction, percentage, and custom formats
|
||||
*/
|
||||
export function formatProgressText(
|
||||
data: ProgressData,
|
||||
plugin: TaskProgressBarPlugin
|
||||
): string {
|
||||
if (!data.total) return "";
|
||||
|
||||
// Calculate percentages
|
||||
const completedPercentage =
|
||||
Math.round((data.completed / data.total) * 10000) / 100;
|
||||
const inProgressPercentage = data.inProgress
|
||||
? Math.round((data.inProgress / data.total) * 10000) / 100
|
||||
: 0;
|
||||
const abandonedPercentage = data.abandoned
|
||||
? Math.round((data.abandoned / data.total) * 10000) / 100
|
||||
: 0;
|
||||
const plannedPercentage = data.planned
|
||||
? Math.round((data.planned / data.total) * 10000) / 100
|
||||
: 0;
|
||||
|
||||
// Create a full data object with percentages for expression evaluation
|
||||
const fullData = {
|
||||
...data,
|
||||
percentages: {
|
||||
completed: completedPercentage,
|
||||
inProgress: inProgressPercentage,
|
||||
abandoned: abandonedPercentage,
|
||||
planned: plannedPercentage,
|
||||
notStarted: data.notStarted
|
||||
? Math.round((data.notStarted / data.total) * 10000) / 100
|
||||
: 0,
|
||||
},
|
||||
};
|
||||
|
||||
// Get status symbols
|
||||
const completedSymbol = "✓";
|
||||
const inProgressSymbol = "⟳";
|
||||
const abandonedSymbol = "✗";
|
||||
const plannedSymbol = "?";
|
||||
|
||||
// Get display mode from settings, with fallbacks for backwards compatibility
|
||||
const displayMode =
|
||||
plugin?.settings.displayMode ||
|
||||
(plugin?.settings.progressBarDisplayMode === "text" ||
|
||||
plugin?.settings.progressBarDisplayMode === "both"
|
||||
? plugin?.settings.showPercentage
|
||||
? "percentage"
|
||||
: "bracketFraction"
|
||||
: "bracketFraction");
|
||||
|
||||
// Process text with template formatting
|
||||
let resultText = "";
|
||||
|
||||
// Handle different display modes
|
||||
switch (displayMode) {
|
||||
case "percentage":
|
||||
// Simple percentage (e.g., "75%")
|
||||
resultText = `${completedPercentage}%`;
|
||||
break;
|
||||
|
||||
case "bracketPercentage":
|
||||
// Percentage with brackets (e.g., "[75%]")
|
||||
resultText = `[${completedPercentage}%]`;
|
||||
break;
|
||||
|
||||
case "fraction":
|
||||
// Simple fraction (e.g., "3/4")
|
||||
resultText = `${data.completed}/${data.total}`;
|
||||
break;
|
||||
|
||||
case "bracketFraction":
|
||||
// Fraction with brackets (e.g., "[3/4]")
|
||||
resultText = `[${data.completed}/${data.total}]`;
|
||||
break;
|
||||
|
||||
case "detailed":
|
||||
// Detailed format showing all task statuses
|
||||
resultText = `[${data.completed}${completedSymbol} ${
|
||||
data.inProgress || 0
|
||||
}${inProgressSymbol} ${data.abandoned || 0}${abandonedSymbol} ${
|
||||
data.planned || 0
|
||||
}${plannedSymbol} / ${data.total}]`;
|
||||
break;
|
||||
|
||||
case "custom":
|
||||
// Handle custom format if available in settings
|
||||
if (plugin?.settings.customFormat) {
|
||||
resultText = plugin.settings.customFormat
|
||||
.replace(/{{COMPLETED}}/g, data.completed.toString())
|
||||
.replace(/{{TOTAL}}/g, data.total.toString())
|
||||
.replace(
|
||||
/{{IN_PROGRESS}}/g,
|
||||
(data.inProgress || 0).toString()
|
||||
)
|
||||
.replace(/{{ABANDONED}}/g, (data.abandoned || 0).toString())
|
||||
.replace(/{{PLANNED}}/g, (data.planned || 0).toString())
|
||||
.replace(
|
||||
/{{NOT_STARTED}}/g,
|
||||
(data.notStarted || 0).toString()
|
||||
)
|
||||
.replace(/{{PERCENT}}/g, completedPercentage.toString())
|
||||
.replace(/{{PROGRESS}}/g, completedPercentage.toString())
|
||||
.replace(
|
||||
/{{PERCENT_IN_PROGRESS}}/g,
|
||||
inProgressPercentage.toString()
|
||||
)
|
||||
.replace(
|
||||
/{{PERCENT_ABANDONED}}/g,
|
||||
abandonedPercentage.toString()
|
||||
)
|
||||
.replace(
|
||||
/{{PERCENT_PLANNED}}/g,
|
||||
plannedPercentage.toString()
|
||||
)
|
||||
.replace(/{{COMPLETED_SYMBOL}}/g, completedSymbol)
|
||||
.replace(/{{IN_PROGRESS_SYMBOL}}/g, inProgressSymbol)
|
||||
.replace(/{{ABANDONED_SYMBOL}}/g, abandonedSymbol)
|
||||
.replace(/{{PLANNED_SYMBOL}}/g, plannedSymbol);
|
||||
} else {
|
||||
resultText = `[${data.completed}/${data.total}]`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "range-based":
|
||||
// Check if custom progress ranges are enabled
|
||||
if (plugin?.settings.customizeProgressRanges) {
|
||||
// Find a matching range for the current percentage
|
||||
const matchingRange = plugin.settings.progressRanges.find(
|
||||
(range) =>
|
||||
completedPercentage >= range.min &&
|
||||
completedPercentage <= range.max
|
||||
);
|
||||
|
||||
// If a matching range is found, use its custom text
|
||||
if (matchingRange) {
|
||||
resultText = matchingRange.text.replace(
|
||||
"{{PROGRESS}}",
|
||||
completedPercentage.toString()
|
||||
);
|
||||
} else {
|
||||
resultText = `${completedPercentage}%`;
|
||||
}
|
||||
} else {
|
||||
resultText = `${completedPercentage}%`;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Legacy behavior for compatibility
|
||||
if (
|
||||
plugin?.settings.progressBarDisplayMode === "text" ||
|
||||
plugin?.settings.progressBarDisplayMode === "both"
|
||||
) {
|
||||
// If using text mode, check if percentage is preferred
|
||||
if (plugin?.settings.showPercentage) {
|
||||
resultText = `${completedPercentage}%`;
|
||||
} else {
|
||||
// Show detailed counts if we have in-progress, abandoned, or planned tasks
|
||||
if (
|
||||
(data.inProgress && data.inProgress > 0) ||
|
||||
(data.abandoned && data.abandoned > 0) ||
|
||||
(data.planned && data.planned > 0)
|
||||
) {
|
||||
resultText = `[${data.completed}✓ ${
|
||||
data.inProgress || 0
|
||||
}⟳ ${data.abandoned || 0}✗ ${data.planned || 0}? / ${
|
||||
data.total
|
||||
}]`;
|
||||
} else {
|
||||
// Simple fraction format with brackets
|
||||
resultText = `[${data.completed}/${data.total}]`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default to bracket fraction if no specific text mode is set
|
||||
resultText = `[${data.completed}/${data.total}]`;
|
||||
}
|
||||
}
|
||||
|
||||
// Process JavaScript expressions enclosed in ${= }
|
||||
resultText = resultText.replace(/\${=(.+?)}/g, (match, expr) => {
|
||||
try {
|
||||
// Create a safe function to evaluate the expression with the data context
|
||||
const evalFunc = new Function("data", `return ${expr}`);
|
||||
return evalFunc(fullData);
|
||||
} catch (error) {
|
||||
console.error("Error evaluating expression:", expr, error);
|
||||
return match; // Return the original match on error
|
||||
}
|
||||
});
|
||||
|
||||
return resultText;
|
||||
}
|
||||
|
||||
class TaskProgressBarWidget extends WidgetType {
|
||||
progressBarEl: HTMLSpanElement;
|
||||
progressBackGroundEl: HTMLDivElement;
|
||||
|
|
@ -161,46 +367,17 @@ class TaskProgressBarWidget extends WidgetType {
|
|||
|
||||
changeNumber() {
|
||||
if (this.plugin?.settings.addNumberToProgressBar) {
|
||||
let text;
|
||||
if (this.plugin?.settings.showPercentage) {
|
||||
// Calculate percentage of completed tasks
|
||||
const percentage =
|
||||
Math.round((this.completed / this.total) * 10000) / 100;
|
||||
|
||||
// Check if custom progress ranges are enabled
|
||||
if (this.plugin?.settings.customizeProgressRanges) {
|
||||
// Find a matching range for the current percentage
|
||||
const matchingRange =
|
||||
this.plugin.settings.progressRanges.find(
|
||||
(range) =>
|
||||
percentage >= range.min &&
|
||||
percentage <= range.max
|
||||
);
|
||||
|
||||
// If a matching range is found, use its custom text
|
||||
if (matchingRange) {
|
||||
text = matchingRange.text.replace(
|
||||
"{{PROGRESS}}",
|
||||
percentage.toString()
|
||||
);
|
||||
} else {
|
||||
text = `${percentage}%`;
|
||||
}
|
||||
} else {
|
||||
text = `${percentage}%`;
|
||||
}
|
||||
} else {
|
||||
// Show detailed counts if we have in-progress, abandoned, or planned tasks
|
||||
if (
|
||||
this.inProgress > 0 ||
|
||||
this.abandoned > 0 ||
|
||||
this.planned > 0
|
||||
) {
|
||||
text = `[${this.completed}✓ ${this.inProgress}⟳ ${this.abandoned}✗ ${this.planned}? / ${this.total}]`;
|
||||
} else {
|
||||
text = `[${this.completed}/${this.total}]`;
|
||||
}
|
||||
}
|
||||
let text = formatProgressText(
|
||||
{
|
||||
completed: this.completed,
|
||||
total: this.total,
|
||||
inProgress: this.inProgress,
|
||||
abandoned: this.abandoned,
|
||||
notStarted: this.notStarted,
|
||||
planned: this.planned,
|
||||
},
|
||||
this.plugin
|
||||
);
|
||||
|
||||
if (!this.numberEl) {
|
||||
this.numberEl = this.progressBarEl.createEl("div", {
|
||||
|
|
@ -259,84 +436,69 @@ class TaskProgressBarWidget extends WidgetType {
|
|||
}
|
||||
}
|
||||
);
|
||||
this.progressBackGroundEl = this.progressBarEl.createEl("div", {
|
||||
cls: "progress-bar-inline-background",
|
||||
});
|
||||
|
||||
this.progressBackGroundEl.toggleClass(
|
||||
"hidden",
|
||||
!this.plugin?.settings.showProgressBar
|
||||
);
|
||||
// Check if graphical progress bar should be shown
|
||||
const showGraphicalBar =
|
||||
this.plugin?.settings.progressBarDisplayMode === "graphical" ||
|
||||
this.plugin?.settings.progressBarDisplayMode === "both";
|
||||
|
||||
// Create elements for each status type
|
||||
this.progressEl = this.progressBackGroundEl.createEl("div", {
|
||||
cls: "progress-bar-inline progress-completed",
|
||||
});
|
||||
|
||||
// Only create these elements if we have tasks of these types
|
||||
if (this.inProgress > 0) {
|
||||
this.inProgressEl = this.progressBackGroundEl.createEl("div", {
|
||||
cls: "progress-bar-inline progress-in-progress",
|
||||
if (showGraphicalBar) {
|
||||
this.progressBackGroundEl = this.progressBarEl.createEl("div", {
|
||||
cls: "progress-bar-inline-background",
|
||||
});
|
||||
}
|
||||
|
||||
if (this.abandoned > 0) {
|
||||
this.abandonedEl = this.progressBackGroundEl.createEl("div", {
|
||||
cls: "progress-bar-inline progress-abandoned",
|
||||
// Create elements for each status type
|
||||
this.progressEl = this.progressBackGroundEl.createEl("div", {
|
||||
cls: "progress-bar-inline progress-completed",
|
||||
});
|
||||
}
|
||||
|
||||
if (this.planned > 0) {
|
||||
this.plannedEl = this.progressBackGroundEl.createEl("div", {
|
||||
cls: "progress-bar-inline progress-planned",
|
||||
});
|
||||
}
|
||||
|
||||
if (this.plugin?.settings.addNumberToProgressBar && this.total) {
|
||||
let text;
|
||||
if (this.plugin?.settings.showPercentage) {
|
||||
const percentage =
|
||||
Math.round((this.completed / this.total) * 10000) / 100;
|
||||
|
||||
// Check if custom progress ranges are enabled
|
||||
if (this.plugin?.settings.customizeProgressRanges) {
|
||||
// Find a matching range for the current percentage
|
||||
const matchingRange =
|
||||
this.plugin.settings.progressRanges.find(
|
||||
(range) =>
|
||||
percentage >= range.min &&
|
||||
percentage <= range.max
|
||||
);
|
||||
|
||||
// If a matching range is found, use its custom text
|
||||
if (matchingRange) {
|
||||
text = matchingRange.text.replace(
|
||||
"{{PROGRESS}}",
|
||||
percentage.toString()
|
||||
);
|
||||
} else {
|
||||
text = `${percentage}%`;
|
||||
}
|
||||
} else {
|
||||
text = `${percentage}%`;
|
||||
}
|
||||
} else {
|
||||
// Show detailed counts if we have in-progress or abandoned tasks
|
||||
if (this.inProgress > 0 || this.abandoned > 0) {
|
||||
text = `[${this.completed}✓ ${this.inProgress}⟳ ${this.abandoned}✗ / ${this.total}]`;
|
||||
} else {
|
||||
text = `[${this.completed}/${this.total}]`;
|
||||
}
|
||||
// Only create these elements if we have tasks of these types
|
||||
if (this.inProgress > 0) {
|
||||
this.inProgressEl = this.progressBackGroundEl.createEl("div", {
|
||||
cls: "progress-bar-inline progress-in-progress",
|
||||
});
|
||||
}
|
||||
|
||||
if (this.abandoned > 0) {
|
||||
this.abandonedEl = this.progressBackGroundEl.createEl("div", {
|
||||
cls: "progress-bar-inline progress-abandoned",
|
||||
});
|
||||
}
|
||||
|
||||
if (this.planned > 0) {
|
||||
this.plannedEl = this.progressBackGroundEl.createEl("div", {
|
||||
cls: "progress-bar-inline progress-planned",
|
||||
});
|
||||
}
|
||||
|
||||
this.changePercentage();
|
||||
}
|
||||
|
||||
// Check if text progress should be shown (either as the only option or together with graphic bar)
|
||||
const showText =
|
||||
this.plugin?.settings.progressBarDisplayMode === "text" ||
|
||||
this.plugin?.settings.progressBarDisplayMode === "both" ||
|
||||
this.plugin?.settings.addNumberToProgressBar;
|
||||
|
||||
if (showText && this.total) {
|
||||
const text = formatProgressText(
|
||||
{
|
||||
completed: this.completed,
|
||||
total: this.total,
|
||||
inProgress: this.inProgress,
|
||||
abandoned: this.abandoned,
|
||||
notStarted: this.notStarted,
|
||||
planned: this.planned,
|
||||
},
|
||||
this.plugin
|
||||
);
|
||||
|
||||
this.numberEl = this.progressBarEl.createEl("div", {
|
||||
cls: "progress-status",
|
||||
text: text,
|
||||
});
|
||||
}
|
||||
|
||||
this.changePercentage();
|
||||
|
||||
return this.progressBarEl;
|
||||
}
|
||||
|
||||
|
|
|
|||
187
src/setting.ts
187
src/setting.ts
|
|
@ -236,18 +236,24 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
|
|||
.setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("Show progress bar"))
|
||||
.setDesc(t("Toggle this to show the progress bar."))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.showProgressBar)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.showProgressBar = value;
|
||||
.setName(t("Progress display mode"))
|
||||
.setDesc(t("Choose how to display task progress"))
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption("none", t("No progress indicators"))
|
||||
.addOption("graphical", t("Graphical progress bar"))
|
||||
.addOption("text", t("Text progress indicator"))
|
||||
.addOption("both", t("Both graphical and text"))
|
||||
.setValue(this.plugin.settings.progressBarDisplayMode)
|
||||
.onChange(async (value: any) => {
|
||||
this.plugin.settings.progressBarDisplayMode = value;
|
||||
this.applySettingsUpdate();
|
||||
this.display();
|
||||
})
|
||||
);
|
||||
|
||||
if (this.plugin.settings.showProgressBar) {
|
||||
// Only show these options if some form of progress bar is enabled
|
||||
if (this.plugin.settings.progressBarDisplayMode !== "none") {
|
||||
new Setting(containerEl)
|
||||
.setName(t("Support hover to show progress info"))
|
||||
.setDesc(
|
||||
|
|
@ -326,7 +332,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
|
|||
.setName(t("Count sub children of current Task"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Toggle this to allow this plugin to count sub tasks when generating progress bar ."
|
||||
"Toggle this to allow this plugin to count sub tasks when generating progress bar."
|
||||
)
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
|
|
@ -338,7 +344,13 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
|
|||
})
|
||||
);
|
||||
|
||||
this.displayNumberToProgressbar(containerEl);
|
||||
// Only show the number settings for modes that include text display
|
||||
if (
|
||||
this.plugin.settings.progressBarDisplayMode === "text" ||
|
||||
this.plugin.settings.progressBarDisplayMode === "both"
|
||||
) {
|
||||
this.displayNumberToProgressbar(containerEl);
|
||||
}
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("Hide progress bars"))
|
||||
|
|
@ -434,30 +446,142 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
|
|||
}
|
||||
}
|
||||
|
||||
// Renamed from showNumberToProgressbar() to match the pattern
|
||||
private displayNumberToProgressbar(containerEl: HTMLElement): void {
|
||||
// Add setting for display mode
|
||||
new Setting(containerEl)
|
||||
.setName(t("Add number to the Progress Bar"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Toggle this to allow this plugin to add tasks number to progress bar."
|
||||
)
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.addNumberToProgressBar)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.addNumberToProgressBar = value;
|
||||
.setName(t("Progress format"))
|
||||
.setDesc(t("Choose how to display the task progress"))
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption("percentage", t("Percentage (75%)"))
|
||||
.addOption(
|
||||
"bracketPercentage",
|
||||
t("Bracketed percentage ([75%])")
|
||||
)
|
||||
.addOption("fraction", t("Fraction (3/4)"))
|
||||
.addOption(
|
||||
"bracketFraction",
|
||||
t("Bracketed fraction ([3/4])")
|
||||
)
|
||||
.addOption("detailed", t("Detailed ([3✓ 1⟳ 0✗ 1? / 5])"))
|
||||
.addOption("custom", t("Custom format"))
|
||||
.addOption("range-based", t("Range-based text"))
|
||||
.setValue(
|
||||
this.plugin.settings.displayMode || "bracketFraction"
|
||||
)
|
||||
.onChange(async (value: any) => {
|
||||
this.plugin.settings.displayMode = value;
|
||||
this.applySettingsUpdate();
|
||||
})
|
||||
);
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
|
||||
if (this.plugin.settings.addNumberToProgressBar) {
|
||||
// Show custom format setting only when custom format is selected
|
||||
if (this.plugin.settings.displayMode === "custom") {
|
||||
new Setting(containerEl)
|
||||
.setName(t("Custom format"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Use placeholders like {{COMPLETED}}, {{TOTAL}}, {{PERCENT}}, etc."
|
||||
)
|
||||
)
|
||||
.addTextArea((text) => {
|
||||
text.inputEl.toggleClass("custom-format-textarea", true);
|
||||
text.setPlaceholder("[{{COMPLETED}}/{{TOTAL}}]")
|
||||
.setValue(
|
||||
this.plugin.settings.customFormat ||
|
||||
"[{{COMPLETED}}/{{TOTAL}}]"
|
||||
)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.customFormat = value;
|
||||
this.applySettingsUpdate();
|
||||
});
|
||||
});
|
||||
|
||||
// Add help text for available placeholders
|
||||
new Setting(containerEl)
|
||||
.setName(t("Available placeholders"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Available placeholders: {{COMPLETED}}, {{TOTAL}}, {{IN_PROGRESS}}, {{ABANDONED}}, {{PLANNED}}, {{NOT_STARTED}}, {{PERCENT}}, {{COMPLETED_SYMBOL}}, {{IN_PROGRESS_SYMBOL}}, {{ABANDONED_SYMBOL}}, {{PLANNED_SYMBOL}}"
|
||||
)
|
||||
);
|
||||
|
||||
// Show examples of advanced formats using expressions
|
||||
containerEl.createEl("div", {
|
||||
cls: "setting-item-name",
|
||||
text: t("Expression examples:"),
|
||||
});
|
||||
|
||||
const exampleContainer = containerEl.createEl("div", {
|
||||
cls: "expression-examples",
|
||||
});
|
||||
|
||||
const examples = [
|
||||
{
|
||||
name: t("Text Progress Bar"),
|
||||
code: '[${="=".repeat(Math.floor(data.percentages.completed/10)) + " ".repeat(10-Math.floor(data.percentages.completed/10))}] {{PERCENT}}%',
|
||||
},
|
||||
{
|
||||
name: t("Emoji Progress Bar"),
|
||||
code: '${="⬛".repeat(Math.floor(data.percentages.completed/10)) + "⬜".repeat(10-Math.floor(data.percentages.completed/10))} {{PERCENT}}%',
|
||||
},
|
||||
{
|
||||
name: t("Color-coded Status"),
|
||||
code: "{{COMPLETED}}/{{TOTAL}} ${=data.percentages.completed < 30 ? '🔴' : data.percentages.completed < 70 ? '🟠' : '🟢'}",
|
||||
},
|
||||
{
|
||||
name: t("Status with Icons"),
|
||||
code: "[{{COMPLETED_SYMBOL}}:{{COMPLETED}} {{IN_PROGRESS_SYMBOL}}:{{IN_PROGRESS}} {{PLANNED_SYMBOL}}:{{PLANNED}} / {{TOTAL}}]",
|
||||
},
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
const exampleItem = exampleContainer.createEl("div", {
|
||||
cls: "expression-example-item",
|
||||
});
|
||||
|
||||
exampleItem.createEl("div", {
|
||||
cls: "expression-example-name",
|
||||
text: example.name,
|
||||
});
|
||||
|
||||
const codeEl = exampleItem.createEl("code", {
|
||||
cls: "expression-example-code",
|
||||
text: example.code,
|
||||
});
|
||||
|
||||
const useButton = exampleItem.createEl("button", {
|
||||
cls: "expression-example-use",
|
||||
text: t("Use"),
|
||||
});
|
||||
|
||||
useButton.addEventListener("click", () => {
|
||||
// 修复:正确设置自定义格式并立即调用applySettingsUpdate
|
||||
this.plugin.settings.customFormat = example.code;
|
||||
this.applySettingsUpdate();
|
||||
|
||||
// 更新展示的输入框内容
|
||||
const inputs = containerEl.querySelectorAll("input");
|
||||
for (const input of Array.from(inputs)) {
|
||||
if (input.placeholder === "[{{COMPLETED}}/{{TOTAL}}]") {
|
||||
input.value = example.code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// Only show legacy percentage toggle for range-based or when displayMode is not set
|
||||
else if (
|
||||
this.plugin.settings.displayMode === "range-based" ||
|
||||
!this.plugin.settings.displayMode
|
||||
) {
|
||||
new Setting(containerEl)
|
||||
.setName(t("Show percentage"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Toggle this to allow this plugin to show percentage in the progress bar."
|
||||
"Toggle this to show percentage instead of completed/total count."
|
||||
)
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
|
|
@ -469,12 +593,16 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
|
|||
})
|
||||
);
|
||||
|
||||
if (this.plugin.settings.showPercentage) {
|
||||
// If percentage display and range-based mode is selected
|
||||
if (
|
||||
this.plugin.settings.showPercentage &&
|
||||
this.plugin.settings.displayMode === "range-based"
|
||||
) {
|
||||
new Setting(containerEl)
|
||||
.setName(t("Customize progress text"))
|
||||
.setName(t("Customize progress ranges"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Toggle this to customize text representation for different progress percentage ranges."
|
||||
"Toggle this to customize the text for different progress ranges."
|
||||
)
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
|
|
@ -486,6 +614,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
|
|||
this.plugin.settings.customizeProgressRanges =
|
||||
value;
|
||||
this.applySettingsUpdate();
|
||||
this.display();
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,11 @@ export function shouldHideProgressBarInLivePriview(
|
|||
plugin: TaskProgressBarPlugin,
|
||||
view: EditorView
|
||||
): boolean {
|
||||
// If progress display mode is set to "none", hide progress bars
|
||||
if (plugin.settings.progressBarDisplayMode === "none") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.settings.hideProgressBarBasedOnConditions) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
40
styles.css
40
styles.css
|
|
@ -1678,3 +1678,43 @@ settings:
|
|||
.task-genius-settings-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Expression examples in settings */
|
||||
.expression-examples {
|
||||
margin-top: 8px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.expression-example-item {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
/* background-color: var(--background-primary); */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.expression-example-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.expression-example-code {
|
||||
padding: 4px 8px;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 4px;
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 0.9em;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.expression-example-use {
|
||||
align-self: flex-end;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.custom-format-textarea {
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue