Compare commits

...

33 commits

Author SHA1 Message Date
L7Cy
0cb10eb8dc 4.6.1 2023-10-23 12:15:37 +09:00
L7Cy
6172371b21 showUntilRegexに変更 2023-10-23 12:14:50 +09:00
L7Cy
6c9a235128 4.6.0 2023-10-23 11:47:49 +09:00
L7Cy
e7e8551679 表示するタスクを制限する機能を追加 2023-10-23 11:46:18 +09:00
L7Cy
4e1610ae66 Merge branch 'master' of https://github.com/L7Cy/obsidian-dynamic-timetable 2023-10-23 10:45:36 +09:00
L7Cy
f4f2169655
Merge pull request #8 from knukio/master
カテゴリを設定しない場合に無限ループが発生する不具合を修正
2023-10-23 10:44:45 +09:00
knukio
f3ec100666 カテゴリを設定しない場合に無限ループが発生する不具合を修正 2023-10-20 17:55:45 +09:00
L7Cy
bff62e9fa5 buffer timeの表記をHHhMMminに変更 2023-09-11 13:25:18 +09:00
L7Cy
7a47c945dd 4.5.0 2023-09-11 11:20:41 +09:00
L7Cy
7fa1fc4e6c デフォルトの値を変更 2023-09-11 11:19:23 +09:00
L7Cy
c94dc7b0cc 説明の記述を変更 2023-09-11 11:17:49 +09:00
L7Cy
6111557b94 辞書の記述を変更 2023-09-11 11:17:25 +09:00
L7Cy
59e71f9d61 テキストエリアの高さを調整 2023-09-11 09:52:36 +09:00
L7Cy
9cb19298ce 4.5.0-beta5 2023-09-10 10:16:03 +09:00
L7Cy
110f78637f 完了時にカスタムURLスキームを実行する機能を追加 2023-09-10 10:11:08 +09:00
L7Cy
5c30e5cf57 カスタムURLスキームの設定を追加 2023-09-10 10:09:26 +09:00
L7Cy
b647a43fdb タグの正規表現を修正 2023-09-03 15:24:41 +09:00
L7Cy
d7d06699ed 4.5.0-beta4 2023-09-02 14:16:00 +09:00
L7Cy
6b47c56ec5 時刻の代わりに残り時間を表示する機能を追加 2023-09-02 14:14:59 +09:00
L7Cy
b2ff931385 タスク行全体にcenterを適用 2023-09-02 13:56:37 +09:00
L7Cy
bba41f4520 コマンドを実行したときにスクロールされない問題を修正 2023-09-02 12:32:54 +09:00
L7Cy
7666536ddd 設定等の見直し 2023-09-02 11:41:07 +09:00
L7Cy
33d1a5739e 先頭にスペースのあるタグのみにマッチするよう修正 2023-09-02 11:31:35 +09:00
L7Cy
68450a8f8b statsが表示されないことがある問題を修正 2023-09-02 11:19:45 +09:00
L7Cy
95b13dbedc 辞書のリロードを追加 2023-09-02 11:04:43 +09:00
L7Cy
92f728207c 4.5.0-beta3 2023-09-01 13:21:38 +09:00
L7Cy
e8091de245 不要な引数の削除 2023-09-01 13:14:07 +09:00
L7Cy
584c7f3191 - [ ] を含めないよう変更 2023-09-01 13:13:04 +09:00
L7Cy
64c47dd54d 4.5.0-beta2 2023-08-31 23:46:17 +09:00
L7Cy
37322ba811 完了時に正しく辞書が更新されない問題を修正 2023-08-31 23:45:42 +09:00
L7Cy
f77b02ce91 4.5.0-beta 2023-08-31 21:16:16 +09:00
L7Cy
acc0f69096 サジェスト用の辞書を作成する機能を追加 2023-08-31 21:15:04 +09:00
L7Cy
f17f8b7537 辞書へのpathの設定を追加 2023-08-31 18:46:36 +09:00
13 changed files with 307 additions and 68 deletions

View file

@ -1,7 +1,7 @@
{
"id": "dynamic-timetable",
"name": "Dynamic Timetable",
"version": "4.4.1",
"version": "4.6.1",
"minAppVersion": "1.2.8",
"description": "Calculate the estimated time of completion from the estimated time of the task and dynamically create a timetable.",
"author": "L7Cy",

View file

@ -1,7 +1,7 @@
{
"id": "dynamic-timetable",
"name": "Dynamic Timetable",
"version": "4.4.1",
"version": "4.6.1",
"minAppVersion": "1.2.8",
"description": "Calculate the estimated time of completion from the estimated time of the task and dynamically create a timetable.",
"author": "L7Cy",

View file

@ -1,6 +1,6 @@
{
"name": "dynamic-timetable",
"version": "4.4.1",
"version": "4.6.1",
"description": "Calculate the estimated time of completion from the estimated time of the task and dynamically create a timetable.",
"main": "main.js",
"scripts": {

View file

@ -4,11 +4,17 @@ type BufferTimeRowProps = {
bufferTime: number | null;
};
const formatTime = (minutes: number): string => {
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
return `${hours}h${remainingMinutes}min`;
};
const BufferTimeRow: React.FC<BufferTimeRowProps> = ({ bufferTime }) => (
<tr className="buffer-time dt-buffer-time">
<td>Buffer Time</td>
<td colSpan={3} style={{ textAlign: 'center' }}>
{bufferTime ? bufferTime : 0}min
{bufferTime !== null ? formatTime(bufferTime) : '0h0min'}
</td>
</tr>
);

View file

@ -23,21 +23,17 @@ export class DynamicTimetableSettingTab extends PluginSettingTab {
'showStartTimeInTaskName'
);
this.createToggleSetting(
'Show Category Names in Task',
'showCategoryNamesInTask',
'If enabled, displays category names within the task name.'
'Show Categories in Task Name',
'showCategoryNamesInTask'
);
this.createToggleSetting('Show Buffer Time Rows', 'showBufferTime');
this.createToggleSetting('Show Completed Tasks', 'showCompletedTasks');
this.createToggleSetting(
'Show Completed Tasks',
'showCompletedTasks',
'If enabled, displays completed tasks in the timetable.'
);
this.createToggleSetting(
'Show Progress Bar',
'showProgressBar',
'If enabled, displays a progress bar based on the top task estimate.'
'Show Remaining Time',
'showRemainingTime',
'Show remaining time instead of the time for current task in progress.'
);
this.createToggleSetting('Show Progress Bar', 'showProgressBar');
if (this.plugin.settings.showProgressBar) {
this.createTextSetting(
'Interval Time (Seconds)',
@ -64,10 +60,43 @@ export class DynamicTimetableSettingTab extends PluginSettingTab {
'Enter a regex that matches the delimiter for a new day.',
'^---$'
);
this.createTextSetting(
'Show Until Regex',
'showUntilRegex',
'Enter a regex. Tasks will be shown until a line matching this regex is found.'
);
const headerNames = Array.isArray(this.plugin.settings.headerNames)
? this.plugin.settings.headerNames.join(', ')
: '';
this.createHeaderNamesSetting(headerNames);
const pathToDictionaryDesc = this.containerEl.createEl('p');
pathToDictionaryDesc.style.marginBlockStart = '0em';
pathToDictionaryDesc.style.marginBlockEnd = '0em';
pathToDictionaryDesc.appendChild(
createEl('span', {
text: 'Enter the path to the custom dictionary file for ',
})
);
pathToDictionaryDesc.appendChild(
createEl('a', {
text: 'Various Complements',
href: 'obsidian://show-plugin?id=various-complements',
})
);
pathToDictionaryDesc.appendText('.');
this.createTextSetting(
'Path to Dictionary for Suggestions',
'pathToDictionary',
pathToDictionaryDesc,
'path/to/dictionary.md'
);
this.createTextAreaSetting(
'Custom URL Scheme',
'customUrlScheme',
'Enter the URL scheme you want to execute when a task is completed. You can use the following placeholders: {{minutes}}, {{seconds}}, {{taskName}}',
'your-app-scheme://doSomething?minutes={{minutes}}&seconds={{seconds}}&taskName={{taskName}}'
);
this.createToggleSetting(
'Apply Background Color by Category (tag)',
'applyBackgroundColorByCategory',
@ -82,7 +111,7 @@ export class DynamicTimetableSettingTab extends PluginSettingTab {
createTextSetting(
name: string,
key: string,
desc?: string,
desc?: any,
placeholder?: string
) {
const setting = new Setting(this.containerEl).setName(name);
@ -101,6 +130,29 @@ export class DynamicTimetableSettingTab extends PluginSettingTab {
});
}
createTextAreaSetting(
name: string,
key: string,
desc?: string,
placeholder?: string
) {
const setting = new Setting(this.containerEl).setName(name);
if (desc) {
setting.setDesc(desc);
}
setting.addTextArea((text) => {
const el = text
.setPlaceholder(placeholder || '')
.setValue((this.plugin.settings[key] as string) || '');
el.inputEl.style.height = '60px';
el.inputEl.addEventListener('blur', async (event) => {
const value = (event.target as HTMLTextAreaElement).value;
await this.plugin.updateSetting(key, value);
});
return el;
});
}
createToggleSetting(name: string, key: string, desc?: string) {
const setting = new Setting(this.containerEl).setName(name);
if (desc) {

View file

@ -53,11 +53,11 @@ export const StatisticsViewComponent = forwardRef((props: Props, ref) => {
const actualBackgroundColors = performanceArray.map((p) => {
const color = categoryBackgroundColors[p.category];
return color.replace(/,\s*([^,]+)\)/, ', 1)');
return color ? color.replace(/,\s*([^,]+)\)/, ', 1)') : '';
});
const estimatedBackgroundColors = performanceArray.map((p) => {
const color = categoryBackgroundColors[p.category];
return color.replace(/,\s*([^,]+)\)/, ', 0.3)');
return color ? color.replace(/,\s*([^,]+)\)/, ', 0.3)') : '';
});
const actualTimes = performanceArray.map((p) => p.actualTime);

View file

@ -82,7 +82,7 @@ export const taskFunctions = (plugin: DynamicTimetable) => {
const originalTaskName = taskMatch[1];
const tags =
line
.match(/#([^\s!#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]+)/gu)
.match(/\s#([^\s!#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]+)/gu)
?.join(' ') || '';
const actualStartTime = new Date(Date.now() - elapsedTime * 60 * 1000);
@ -135,6 +135,7 @@ export const taskFunctions = (plugin: DynamicTimetable) => {
let content = await plugin.app.vault.cachedRead(plugin.targetFile);
const elapsedTime = getElapsedTime(content);
await updateDictionaryFile(elapsedTime);
const taskUpdate: TaskUpdate = { task, elapsedTime, remainingTime };
content = updateTaskInContent(content, taskUpdate);
@ -150,6 +151,37 @@ export const taskFunctions = (plugin: DynamicTimetable) => {
const completeTask = async (task: Task) => {
await updateTask(task, undefined);
if (!plugin.targetFile) {
return;
}
let content = await plugin.app.vault.cachedRead(plugin.targetFile);
const taskParser = TaskParser.fromSettings(plugin.settings);
let tasks: Task[] = taskParser.filterAndParseTasks(content);
const nextUncompletedTask = tasks.find((t) => !t.isCompleted && t !== task);
if (nextUncompletedTask && plugin.settings.customUrlScheme) {
let customUrl = plugin.settings.customUrlScheme;
const nextTaskName = nextUncompletedTask.task;
const nextTaskEstimate = nextUncompletedTask.estimate;
if (!nextTaskEstimate) {
return;
}
const nextTaskMinutes = parseInt(nextTaskEstimate);
const nextTaskSeconds = nextTaskMinutes * 60;
customUrl = customUrl.replace('{{minutes}}', nextTaskMinutes.toString());
customUrl = customUrl.replace('{{seconds}}', nextTaskSeconds.toString());
customUrl = customUrl.replace(
'{{taskName}}',
encodeURIComponent(nextTaskName)
);
window.open(customUrl);
}
};
const interruptTask = async () => {
@ -173,6 +205,102 @@ export const taskFunctions = (plugin: DynamicTimetable) => {
await updateTask(firstUncompletedTask, remainingTime);
};
const updateDictionaryFile = async (elapsedTime: number) => {
if (!plugin.targetFile) {
return;
}
const targetFileContent = await plugin.app.vault.cachedRead(
plugin.targetFile
);
const taskParser = TaskParser.fromSettings(plugin.settings);
let tasks: Task[] = taskParser.filterAndParseTasks(targetFileContent);
const firstUncompletedTask = tasks.find((task) => !task.isCompleted);
if (!firstUncompletedTask) return;
const dictionaryPath = plugin.settings.pathToDictionary;
const dictionaryFile = plugin.app.metadataCache.getFirstLinkpathDest(
dictionaryPath,
'/'
);
if (!dictionaryFile) {
return;
}
let content = await plugin.app.vault.cachedRead(dictionaryFile);
const escapeRegExp = (string: string) => {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
const taskLineRegex = new RegExp(
`^${escapeRegExp(firstUncompletedTask.originalTaskName)} ${
plugin.settings.taskEstimateDelimiter
}(.+)$`,
'm'
);
const match = content.match(taskLineRegex);
let recentTimes: number[] = [];
let trimmedMean = elapsedTime;
let median = elapsedTime;
if (match) {
const stats = match[1].split(',');
if (stats.length > 2 && stats[2]) {
const timesString = stats[2].split('%%')[1];
if (timesString) {
recentTimes = timesString.split('|').map(parseFloat);
}
}
}
recentTimes.push(elapsedTime);
if (recentTimes.length > 20) {
recentTimes.shift();
}
trimmedMean = calculateTrimmedMean(recentTimes);
median = calculateMedian(recentTimes);
const newLine = `${firstUncompletedTask.originalTaskName} ${
plugin.settings.taskEstimateDelimiter
}${trimmedMean},Trimmed Mean: ${trimmedMean} Median: ${median} Recent: ${elapsedTime},${
firstUncompletedTask.task
}%%${recentTimes.join('|')}%%`;
if (match) {
content = content.replace(taskLineRegex, newLine);
} else {
content += '\n' + newLine;
}
await plugin.app.vault.modify(dictionaryFile, content);
await (plugin.app as any).commands.executeCommandById(
'various-complements:reload-custom-dictionaries'
);
};
const calculateMedian = (values: number[]): number => {
const sorted = [...values].sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
const median =
sorted.length % 2 !== 0
? sorted[mid]
: (sorted[mid - 1] + sorted[mid]) / 2;
return Math.ceil(median);
};
const calculateTrimmedMean = (values: number[]): number => {
if (values.length <= 2) return Math.ceil(calculateMedian(values));
const sorted = [...values].sort((a, b) => a - b);
sorted.pop();
sorted.shift();
const trimmedMean = sorted.reduce((a, b) => a + b) / sorted.length;
return Math.ceil(trimmedMean);
};
return {
initializeTasks,
completeTask,

View file

@ -1,6 +1,7 @@
import { DynamicTimetableSettings } from './main';
export interface Task {
originalTaskName: string;
task: string;
startTime: Date | null;
estimate: string | null;
@ -12,6 +13,7 @@ export interface Task {
export class TaskParser {
private dateDelimiter: RegExp;
private showUntilRegex: RegExp;
constructor(
private separator: string,
@ -19,9 +21,13 @@ export class TaskParser {
dateDelimiter: string,
private showStartTimeInTaskName: boolean,
private showEstimateInTaskName: boolean,
private showCategoryNamesInTask: boolean
private showCategoryNamesInTask: boolean,
showUntilRegex: string
) {
this.dateDelimiter = dateDelimiter ? new RegExp(dateDelimiter) : /(?!x)x/;
this.showUntilRegex = showUntilRegex
? new RegExp(showUntilRegex)
: /(?!x)x/;
}
static fromSettings(settings: DynamicTimetableSettings): TaskParser {
@ -31,7 +37,8 @@ export class TaskParser {
settings.dateDelimiter,
settings.showStartTimeInTaskName,
settings.showEstimateInTaskName,
settings.showCategoryNamesInTask
settings.showCategoryNamesInTask,
settings.showUntilRegex
);
}
@ -40,18 +47,24 @@ export class TaskParser {
let firstUncompletedTaskFound = false;
let nextDay = 0;
let dateDelimiterFound = false;
let stopParsing = false;
const yamlStartTime = this.getYamlStartTime(content);
const tasks = content
.split('\n')
.map((line) => line.trim())
.reduce((acc: Task[], task) => {
if (stopParsing) return acc;
if (this.isDateDelimiterLine(task)) {
if (firstUncompletedTaskFound) {
dateDelimiterFound = true;
}
return acc;
}
if (this.showUntilRegex.test(task)) {
stopParsing = true;
return acc;
}
if (
!task.startsWith('- [ ]') &&
@ -68,7 +81,7 @@ export class TaskParser {
task.startsWith('- [x]') ||
task.startsWith('+ [x]') ||
task.startsWith('* [x]');
const taskName = this.parseTaskName(task);
const { taskName, originalTaskName } = this.parseTaskName(task);
if (dateDelimiterFound) {
nextDay++;
@ -97,6 +110,7 @@ export class TaskParser {
if (estimate) {
acc.push({
originalTaskName: originalTaskName,
task: taskName,
startTime: startTime,
estimate: estimate,
@ -117,10 +131,26 @@ export class TaskParser {
return this.dateDelimiter.test(line);
}
public parseTaskName(taskName: string): string {
public parseTaskName(taskName: string): {
taskName: string;
originalTaskName: string;
} {
const taskNameRegex = /^[-+*]\s*\[\s*.\s*\]\s*/;
const linkRegex = /\[\[([^\[\]]*\|)?([^\[\]]+)\]\]/g;
const markdownLinkRegex = /\[([^\[\]]+)\]\(.+?\)/g;
const estimateRegex = new RegExp(`\\${this.separator}\\s*\\d+\\s*`);
const startTimeRegex = new RegExp(
`\\${this.startTimeDelimiter}\\s*(?:\\d{4}-\\d{2}-\\d{2}T)?(\\d{1,2}:?\\d{2})`
);
const categoryRegex = /\s#([^\s!#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]+)/gu;
let originalTaskName = taskName;
originalTaskName = originalTaskName
.replace(taskNameRegex, '')
.replace(estimateRegex, '')
.replace(startTimeRegex, '')
.replace(categoryRegex, '')
.trim();
taskName = taskName
.replace(taskNameRegex, '')
@ -129,14 +159,8 @@ export class TaskParser {
.replace(markdownLinkRegex, '$1')
.trim();
const startTimeRegex = new RegExp(
`\\${this.startTimeDelimiter}\\s*(?:\\d{4}-\\d{2}-\\d{2}T)?(\\d{1,2}:?\\d{2})`
);
if (!this.showCategoryNamesInTask) {
taskName = taskName
.replace(/#([^\s!#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]+)/gu, '')
.trim();
taskName = taskName.replace(categoryRegex, '').trim();
}
if (this.showStartTimeInTaskName) {
@ -149,11 +173,10 @@ export class TaskParser {
}
if (!this.showEstimateInTaskName) {
const estimateRegex = new RegExp(`\\${this.separator}\\s*\\d+\\s*`);
taskName = taskName.replace(estimateRegex, '').trim();
}
return taskName;
return { taskName, originalTaskName };
}
public parseStartTime(task: string, nextDay: number): Date | null {
@ -201,7 +224,7 @@ export class TaskParser {
}
private parseCategories(taskName: string): string[] {
const tagRegex = /#([^\s!#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]+)/gu;
const tagRegex = /\s#([^\s!#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]+)/gu;
const categories = [];
let match;
while ((match = tagRegex.exec(taskName)) !== null) {

View file

@ -9,6 +9,8 @@ type TaskRowProps = {
firstUncompletedTaskRef: React.MutableRefObject<HTMLTableRowElement | null> | null;
categoryBackgroundColors: Record<string, string>;
allTasksCompleted: boolean;
duration: number;
estimate: number;
};
const formatDateToTime = (date: Date) => {
@ -17,6 +19,17 @@ const formatDateToTime = (date: Date) => {
return `${hours}:${minutes}`;
};
const formatToHHMMSS = (seconds: number) => {
const hours = Math.floor(seconds / 3600)
.toString()
.padStart(2, '0');
const minutes = Math.floor((seconds % 3600) / 60)
.toString()
.padStart(2, '0');
const remainingSeconds = (seconds % 60).toString().padStart(2, '0');
return `${hours}:${minutes}:${remainingSeconds}`;
};
const createCategoryClasses = (categories: string[]): string => {
return categories.map((category) => `dt-category-${category}`).join(' ');
};
@ -28,19 +41,26 @@ const TaskRow: React.FC<TaskRowProps> = ({
firstUncompletedTaskRef,
categoryBackgroundColors,
allTasksCompleted,
duration,
estimate,
}) => {
let bufferClass = '';
if (
task.originalStartTime &&
bufferTime !== null &&
0 <= bufferTime &&
!task.isCompleted
(task.originalStartTime &&
bufferTime !== null &&
0 <= bufferTime &&
!task.isCompleted) ||
(0 <= estimate - duration && firstUncompletedTaskRef)
) {
bufferClass = 'on-time';
} else if (bufferTime !== null && bufferTime < 0 && !task.isCompleted) {
} else if (
(bufferTime !== null && bufferTime < 0 && !task.isCompleted) ||
(estimate - duration < 0 && firstUncompletedTaskRef)
) {
bufferClass = 'late';
}
const remainingTimeSeconds = Math.floor((estimate - duration) / 1000);
const categoryClasses = createCategoryClasses(task.categories);
const originalBackgroundColor =
categoryBackgroundColors[task.categories[0]] || '';
@ -58,22 +78,31 @@ const TaskRow: React.FC<TaskRowProps> = ({
return (
<tr
ref={task.isCompleted ? null : firstUncompletedTaskRef}
className={`${bufferClass} ${
className={`dt-task-row ${bufferClass} ${
!allTasksCompleted && task.isCompleted ? 'dt-completed' : ''
} ${categoryClasses}`}
style={style}>
<td>{task.task}</td>
{plugin.settings.showEstimate && (
<td style={{ textAlign: 'center' }}>{task.estimate}</td>
)}
{plugin.settings.showStartTime && (
{plugin.settings.showEstimate &&
!(plugin.settings.showRemainingTime && firstUncompletedTaskRef) && (
<td style={{ textAlign: 'center' }}>{task.estimate}</td>
)}
{plugin.settings.showStartTime &&
!(plugin.settings.showRemainingTime && firstUncompletedTaskRef) && (
<td style={{ textAlign: 'center' }}>
{task.startTime ? formatDateToTime(task.startTime) : ''}
</td>
)}
{!(plugin.settings.showRemainingTime && firstUncompletedTaskRef) && (
<td style={{ textAlign: 'center' }}>
{task.startTime ? formatDateToTime(task.startTime) : ''}
{task.endTime ? formatDateToTime(task.endTime) : ''}
</td>
)}
{plugin.settings.showRemainingTime && firstUncompletedTaskRef && (
<td colSpan={3} style={{ textAlign: 'center' }}>
{formatToHHMMSS(Math.abs(remainingTimeSeconds))}
</td>
)}
<td style={{ textAlign: 'center' }}>
{task.endTime ? formatDateToTime(task.endTime) : ''}
</td>
</tr>
);
};

View file

@ -2,23 +2,19 @@ import React from 'react';
import { createRoot } from 'react-dom/client';
import { WorkspaceLeaf, ItemView } from 'obsidian';
import DynamicTimetable from './main';
import TimetableViewComponent, {
TimetableViewComponentRef,
} from './TimetableViewComponent';
import TimetableViewComponent from './TimetableViewComponent';
import { CommandsManager } from './Commands';
export class TimetableView extends ItemView {
private readonly plugin: DynamicTimetable;
private componentRef: React.RefObject<TimetableViewComponentRef>;
private root: any;
private commandsManager: CommandsManager;
constructor(leaf: WorkspaceLeaf, plugin: DynamicTimetable) {
super(leaf);
this.plugin = plugin;
this.componentRef = React.createRef<TimetableViewComponentRef>();
this.commandsManager = new CommandsManager(plugin);
this.icon = "table";
this.icon = 'table';
}
getViewType(): string {
@ -33,7 +29,7 @@ export class TimetableView extends ItemView {
this.root = createRoot(this.containerEl);
this.root.render(
<TimetableViewComponent
ref={this.componentRef}
ref={this.plugin.timetableViewComponentRef}
plugin={this.plugin}
commandsManager={this.commandsManager}
/>
@ -47,8 +43,8 @@ export class TimetableView extends ItemView {
}
async update() {
if (this.componentRef.current) {
this.componentRef.current.update();
if (this.plugin.timetableViewComponentRef.current) {
this.plugin.timetableViewComponentRef.current.update();
}
}
}

View file

@ -12,7 +12,7 @@ import ProgressBar from './ProgressBar';
import { CommandsManager } from './Commands';
import BufferTimeRow from './BufferTimeRow';
import TaskRow from './TaskRow';
import { MarkdownView, Notice } from 'obsidian';
import { Notice } from 'obsidian';
import {
convertHexToHSLA,
getHSLAColorForCategory,
@ -70,10 +70,6 @@ const TimetableViewComponent = forwardRef<
const performScroll = () => {
if (firstUncompletedTaskRef.current && containerRef.current) {
const activeView = plugin.app.workspace.getActiveViewOfType(MarkdownView);
if (activeView?.file === plugin.targetFile) {
return;
}
const containerHeight = containerRef.current.offsetHeight;
const taskOffsetTop = firstUncompletedTaskRef.current.offsetTop;
const scrollToPosition = taskOffsetTop - containerHeight / 5;
@ -119,10 +115,10 @@ const TimetableViewComponent = forwardRef<
}
newBackgroundColors[category] = color;
document.documentElement.style.setProperty(`--${className}-bg`, color);
if (!plugin.isCategoryColorsReady) {
plugin.isCategoryColorsReady = true;
}
});
if (!plugin.isCategoryColorsReady) {
plugin.isCategoryColorsReady = true;
}
});
setCategoryBackgroundColors(newBackgroundColors);
@ -199,7 +195,7 @@ const TimetableViewComponent = forwardRef<
useEffect(() => {
performScroll();
}, [tasks]);
}, [plugin.targetFile]);
useEffect(() => {
updateBackgroundColors();
@ -276,6 +272,8 @@ const TimetableViewComponent = forwardRef<
task === firstUncompletedTask ? firstUncompletedTaskRef : null
}
allTasksCompleted={allTasksCompleted}
duration={progressDuration}
estimate={progressEstimate}
/>
);

View file

@ -27,6 +27,10 @@ export interface DynamicTimetableSettings {
showCategoryNamesInTask: boolean;
categoryColors: { category: string; color: string }[];
categoryTransparency: number;
pathToDictionary: string;
showRemainingTime: boolean;
customUrlScheme: string;
showUntilRegex: string;
[key: string]:
| string
| boolean
@ -68,6 +72,10 @@ export default class DynamicTimetable extends Plugin {
showCategoryNamesInTask: false,
categoryColors: [],
categoryTransparency: 0.3,
pathToDictionary: '',
showRemainingTime: true,
customUrlScheme: '',
showUntilRegex: '',
};
async onload() {

View file

@ -1,6 +1,5 @@
.on-time td,
.late td {
vertical-align: text-top;
.dt-task-row {
vertical-align: center;
}
.late {