Compare commits

...

7 commits

Author SHA1 Message Date
Ell 48d576aaa2 0.2.0 2024-02-26 15:37:54 +01:00
Ell 701be28601 use the hover background color 2024-02-26 15:36:28 +01:00
Ell 1d90a17e69 added timestamp display for durations
closes #20
2024-02-26 15:21:24 +01:00
Ell 7633ac8144 hover highlighting for the table 2024-02-26 14:56:31 +01:00
Ell 7e108a85cb right-align the buttons 2024-02-26 14:53:33 +01:00
Ell 5bd9fefe60 only indent the names, not the values 2024-02-26 14:41:25 +01:00
Ell 3469232d3b fixed some issues with how and when entry content is displayed
closes #36
2024-02-26 14:39:48 +01:00
10 changed files with 87 additions and 45 deletions

View file

@ -1,7 +1,7 @@
{ {
"id": "simple-time-tracker", "id": "simple-time-tracker",
"name": "Super Simple Time Tracker", "name": "Super Simple Time Tracker",
"version": "0.1.8", "version": "0.2.0",
"minAppVersion": "1.2.8", "minAppVersion": "1.2.8",
"description": "Multi-purpose time trackers for your notes!", "description": "Multi-purpose time trackers for your notes!",
"author": "Ellpeck", "author": "Ellpeck",

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "simple-time-tracker", "name": "simple-time-tracker",
"version": "0.1.7", "version": "0.2.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "simple-time-tracker", "name": "simple-time-tracker",
"version": "0.1.7", "version": "0.2.0",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.6", "@types/node": "^16.11.6",

View file

@ -1,6 +1,6 @@
{ {
"name": "simple-time-tracker", "name": "simple-time-tracker",
"version": "0.1.8", "version": "0.2.0",
"description": "Multi-purpose time trackers for your notes!", "description": "Multi-purpose time trackers for your notes!",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View file

@ -52,6 +52,17 @@ export class SimpleTimeTrackerSettingsTab extends PluginSettingTab {
}); });
}); });
new Setting(this.containerEl)
.setName("Timestamp Durations")
.setDesc("Whether durations should be displayed in a timestamp format (12:15:01) rather than the default duration format (12h 15m 1s).")
.addToggle(t => {
t.setValue(this.plugin.settings.timestampDurations);
t.onChange(async v => {
this.plugin.settings.timestampDurations = v;
await this.plugin.saveSettings();
});
});
new Setting(this.containerEl) new Setting(this.containerEl)
.setName("Display Segments in Reverse Order") .setName("Display Segments in Reverse Order")
.setDesc("Whether older tracker segments should be displayed towards the bottom of the tracker, rather than the top.") .setDesc("Whether older tracker segments should be displayed towards the bottom of the tracker, rather than the top.")

View file

@ -3,7 +3,8 @@ export const defaultSettings: SimpleTimeTrackerSettings = {
editableTimestampFormat: "YYYY-MM-DD HH:mm:ss", editableTimestampFormat: "YYYY-MM-DD HH:mm:ss",
csvDelimiter: ",", csvDelimiter: ",",
fineGrainedDurations: true, fineGrainedDurations: true,
reverseSegmentOrder: false reverseSegmentOrder: false,
timestampDurations: false
}; };
export interface SimpleTimeTrackerSettings { export interface SimpleTimeTrackerSettings {
@ -13,5 +14,6 @@ export interface SimpleTimeTrackerSettings {
csvDelimiter: string; csvDelimiter: string;
fineGrainedDurations: boolean; fineGrainedDurations: boolean;
reverseSegmentOrder: boolean; reverseSegmentOrder: boolean;
timestampDurations: boolean;
} }

View file

@ -42,7 +42,7 @@ export function loadTracker(json: string): Tracker {
} }
export function displayTracker(tracker: Tracker, element: HTMLElement, file: string, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings): void { export function displayTracker(tracker: Tracker, element: HTMLElement, file: string, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings): void {
element.classList.add("simple-time-tracker-container"); element.addClass("simple-time-tracker-container");
// add start/stop controls // add start/stop controls
let running = isRunning(tracker); let running = isRunning(tracker);
let btn = new ButtonComponent(element) let btn = new ButtonComponent(element)
@ -215,29 +215,37 @@ function unformatEditableTimestamp(formatted: string, settings: SimpleTimeTracke
function formatDuration(totalTime: number, settings: SimpleTimeTrackerSettings): string { function formatDuration(totalTime: number, settings: SimpleTimeTrackerSettings): string {
let ret = ""; let ret = "";
let duration = moment.duration(totalTime); let duration = moment.duration(totalTime);
let hours: number; let hours = settings.fineGrainedDurations ? duration.hours() : Math.floor(duration.asHours());
if (settings.fineGrainedDurations) {
if (duration.years() > 0) if (settings.timestampDurations) {
ret += duration.years() + "y "; if (settings.fineGrainedDurations) {
if (duration.months() > 0) let days = Math.floor(duration.asDays());
ret += duration.months() + "M "; if (days > 0)
if (duration.days() > 0) ret += days + ".";
ret += duration.days() + "d "; }
hours = duration.hours(); ret += `${hours.toString().padStart(2, "0")}:${duration.minutes().toString().padStart(2, "0")}:${duration.seconds().toString().padStart(2, "0")}`;
} else { } else {
hours = Math.floor(duration.asHours()); if (settings.fineGrainedDurations) {
let years = Math.floor(duration.asYears());
if (years > 0)
ret += years + "y ";
if (duration.months() > 0)
ret += duration.months() + "M ";
if (duration.days() > 0)
ret += duration.days() + "d ";
}
if (hours > 0)
ret += hours + "h ";
if (duration.minutes() > 0)
ret += duration.minutes() + "m ";
ret += duration.seconds() + "s";
} }
if (hours > 0)
ret += hours + "h ";
if (duration.minutes() > 0)
ret += duration.minutes() + "m ";
ret += duration.seconds() + "s";
return ret; return ret;
} }
function fixLegacyTimestamps(entries: Entry[]): void { function fixLegacyTimestamps(entries: Entry[]): void {
for (let entry of entries) { for (let entry of entries) {
if (!isNaN(+entry.startTime)) if (entry.startTime && !isNaN(+entry.startTime))
entry.startTime = moment.unix(+entry.startTime).toISOString(); entry.startTime = moment.unix(+entry.startTime).toISOString();
if (entry.endTime && !isNaN(+entry.endTime)) if (entry.endTime && !isNaN(+entry.endTime))
entry.endTime = moment.unix(+entry.endTime).toISOString(); entry.endTime = moment.unix(+entry.endTime).toISOString();
@ -305,7 +313,7 @@ class EditableField {
this.label = this.cell.createEl("span", {text: value}); this.label = this.cell.createEl("span", {text: value});
this.label.style.marginLeft = `${indent}em`; this.label.style.marginLeft = `${indent}em`;
this.box = new TextComponent(this.cell).setValue(value); this.box = new TextComponent(this.cell).setValue(value);
this.box.inputEl.classList.add("simple-time-tracker-input"); this.box.inputEl.addClass("simple-time-tracker-input");
this.box.inputEl.hide(); this.box.inputEl.hide();
} }
@ -331,8 +339,8 @@ class EditableField {
class EditableTimestampField extends EditableField { class EditableTimestampField extends EditableField {
settings: SimpleTimeTrackerSettings; settings: SimpleTimeTrackerSettings;
constructor(row: HTMLTableRowElement, indent: number, value: string, settings: SimpleTimeTrackerSettings) { constructor(row: HTMLTableRowElement, value: string, settings: SimpleTimeTrackerSettings) {
super(row, indent, value ? formatTimestamp(value, settings) : ""); super(row, 0, value ? formatTimestamp(value, settings) : "");
this.settings = settings; this.settings = settings;
} }
@ -362,30 +370,32 @@ class EditableTimestampField extends EditableField {
} }
} }
function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableElement, newSegmentNameBox: TextComponent, running: boolean, file: string, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings, indent: number): void { function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableElement, newSegmentNameBox: TextComponent, trackerRunning: boolean, file: string, getSectionInfo: () => MarkdownSectionInformation, settings: SimpleTimeTrackerSettings, indent: number): void {
let entryRunning = getRunningEntry(tracker.entries) == entry;
let row = table.createEl("tr"); let row = table.createEl("tr");
let nameField = new EditableField(row, indent, entry.name); let nameField = new EditableField(row, indent, entry.name);
let startField = new EditableTimestampField(row, indent, (entry.startTime), settings); let startField = new EditableTimestampField(row, (entry.startTime), settings);
let endField = new EditableTimestampField(row, indent, (entry.endTime), settings); let endField = new EditableTimestampField(row, (entry.endTime), settings);
row.createEl("td", {text: entry.endTime || entry.subEntries ? formatDuration(getDuration(entry), settings) : ""}); row.createEl("td", {text: entry.endTime || entry.subEntries ? formatDuration(getDuration(entry), settings) : ""});
let entryButtons = row.createEl("td"); let entryButtons = row.createEl("td");
if (!running) { entryButtons.addClass("simple-time-tracker-table-buttons");
new ButtonComponent(entryButtons) new ButtonComponent(entryButtons)
.setClass("clickable-icon") .setClass("clickable-icon")
.setIcon(`lucide-play`) .setIcon(`lucide-play`)
.setTooltip("Continue") .setTooltip("Continue")
.onClick(async () => { .setDisabled(trackerRunning)
startSubEntry(entry, newSegmentNameBox.getValue()); .onClick(async () => {
await saveTracker(tracker, this.app, file, getSectionInfo()); startSubEntry(entry, newSegmentNameBox.getValue());
}); await saveTracker(tracker, this.app, file, getSectionInfo());
} });
let editButton = new ButtonComponent(entryButtons) let editButton = new ButtonComponent(entryButtons)
.setClass("clickable-icon") .setClass("clickable-icon")
.setTooltip("Edit") .setTooltip("Edit")
.setIcon("lucide-pencil") .setIcon("lucide-pencil")
.setDisabled(entryRunning)
.onClick(async () => { .onClick(async () => {
if (nameField.editing()) { if (nameField.editing()) {
entry.name = nameField.endEdit(); entry.name = nameField.endEdit();
@ -397,8 +407,11 @@ function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableEle
editButton.setIcon("lucide-pencil"); editButton.setIcon("lucide-pencil");
} else { } else {
nameField.beginEdit(entry.name); nameField.beginEdit(entry.name);
startField.beginEdit((entry.startTime)); // only allow editing start and end times if we don't have sub entries
endField.beginEdit((entry.endTime)); if (!entry.subEntries) {
startField.beginEdit(entry.startTime);
endField.beginEdit(entry.endTime);
}
editButton.setIcon("lucide-check"); editButton.setIcon("lucide-check");
} }
}); });
@ -406,6 +419,7 @@ function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableEle
.setClass("clickable-icon") .setClass("clickable-icon")
.setTooltip("Remove") .setTooltip("Remove")
.setIcon("lucide-trash") .setIcon("lucide-trash")
.setDisabled(entryRunning)
.onClick(async () => { .onClick(async () => {
removeEntry(tracker.entries, entry); removeEntry(tracker.entries, entry);
await saveTracker(tracker, this.app, file, getSectionInfo()); await saveTracker(tracker, this.app, file, getSectionInfo());
@ -413,6 +427,6 @@ function addEditableTableRow(tracker: Tracker, entry: Entry, table: HTMLTableEle
if (entry.subEntries) { if (entry.subEntries) {
for (let sub of orderedEntries(entry.subEntries, settings)) for (let sub of orderedEntries(entry.subEntries, settings))
addEditableTableRow(tracker, sub, table, newSegmentNameBox, running, file, getSectionInfo, settings, indent + 1); addEditableTableRow(tracker, sub, table, newSegmentNameBox, trackerRunning, file, getSectionInfo, settings, indent + 1);
} }
} }

View file

@ -60,6 +60,7 @@
.simple-time-tracker-table td, .simple-time-tracker-table td,
.simple-time-tracker-table th { .simple-time-tracker-table th {
vertical-align: middle;
border: none; border: none;
} }
@ -71,3 +72,11 @@
max-width: 150px; max-width: 150px;
min-width: 100px; min-width: 100px;
} }
.simple-time-tracker-table-buttons {
text-align: right !important;
}
.simple-time-tracker-table tr:hover {
background-color: var(--background-modifier-hover);
}

View file

@ -2,6 +2,7 @@
"timestampFormat": "YY-MM-DD hh:mm:ss", "timestampFormat": "YY-MM-DD hh:mm:ss",
"editableTimestampFormat": "YYYY-MM-DD HH:mm:ss", "editableTimestampFormat": "YYYY-MM-DD HH:mm:ss",
"csvDelimiter": ",", "csvDelimiter": ",",
"fineGrainedDurations": false, "fineGrainedDurations": true,
"reverseSegmentOrder": true "reverseSegmentOrder": false,
"timestampDurations": true
} }

View file

@ -2,5 +2,9 @@
More notes for my cool project! This note shows that we can correctly display accumulated time that lasts longer than a 24 hour day! More notes for my cool project! This note shows that we can correctly display accumulated time that lasts longer than a 24 hour day!
```simple-time-tracker ```simple-time-tracker
{"entries":[{"name":"test","startTime":"2020-08-01T07:00:00.000Z","endTime":"2021-08-02T07:00:00.000Z","subEntries":null},{"name":"test","startTime":"2021-08-01T07:00:00.000Z","endTime":"2021-10-02T07:00:00.000Z","subEntries":null},{"name":"test","startTime":"1970-01-01T00:00:00.000Z","endTime":null,"subEntries":[{"name":"Part 1","startTime":"2022-08-01T07:00:00.000Z","endTime":"2022-08-02T07:00:00.000Z","subEntries":null},{"name":"Part 2","startTime":"2024-02-26T13:19:40.629Z","endTime":"2024-02-26T13:19:43.713Z","subEntries":null},{"name":"Part 3","startTime":"2024-02-26T13:23:51.939Z","endTime":"2024-02-26T13:23:54.232Z","subEntries":null}]},{"name":"test","startTime":"2022-10-01T12:30:10.000Z","endTime":"2022-10-01T13:40:05.000Z","subEntries":null},{"name":"Segment 5","startTime":"2023-05-23T16:16:56.000Z","endTime":"2023-05-23T16:16:59.000Z","subEntries":null}]} {"entries":[{"name":"test","startTime":"2020-08-01T07:00:00.000Z","endTime":"2021-08-02T07:00:00.000Z","subEntries":null},{"name":"test","startTime":"2021-08-01T07:00:00.000Z","endTime":"2021-10-02T07:00:00.000Z","subEntries":null},{"name":"test","startTime":"1970-01-01T00:00:00.000Z","endTime":null,"subEntries":[{"name":"Part 1","startTime":"2022-08-01T07:00:00.000Z","endTime":"2022-08-02T07:00:00.000Z","subEntries":null},{"name":"Part 2","startTime":"2024-02-26T13:19:40.629Z","endTime":"2024-02-26T13:19:43.713Z","subEntries":null},{"name":"Part 3","startTime":"1970-01-01T00:00:00.000Z","endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-02-26T13:23:51.939Z","endTime":"2024-02-26T13:23:54.232Z","subEntries":null},{"name":"Part 2","startTime":"2024-02-26T13:27:34.397Z","endTime":"2024-02-26T13:27:49.282Z","subEntries":null}]},{"name":"Part 4","startTime":"2024-02-26T13:29:06.983Z","endTime":"2024-02-26T13:29:20.770Z","subEntries":null}]},{"name":"test","startTime":"2022-10-01T12:30:10.000Z","endTime":"2022-10-01T13:40:05.000Z","subEntries":null},{"name":"Segment 5","startTime":"1970-01-01T00:00:00.000Z","endTime":null,"subEntries":[{"name":"Part 1","startTime":"2023-05-23T16:16:56.000Z","endTime":"2023-05-23T16:16:59.000Z","subEntries":null},{"name":"Part 2","startTime":"1970-01-01T00:00:00.000Z","endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-02-26T13:30:39.632Z","endTime":"2024-02-26T13:30:56.290Z","subEntries":null},{"name":"Part 2","startTime":"2024-02-26T13:30:57.000Z","endTime":"2024-02-26T13:31:00.000Z","subEntries":null}]},{"name":"Part 3","startTime":"2024-02-26T13:34:18.537Z","endTime":"2024-02-26T13:34:21.169Z","subEntries":null}]}]}
```
```simple-time-tracker
{"entries":[{"name":"Segment 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":null,"endTime":null,"subEntries":[{"name":"Part 1","startTime":"2024-02-26T13:37:59.292Z","endTime":"2024-02-26T13:38:01.437Z","subEntries":null},{"name":"Part 2","startTime":"2024-02-26T14:04:14.156Z","endTime":"2024-02-26T14:04:30.576Z","subEntries":null}]},{"name":"Part 2","startTime":"2024-02-26T13:38:16.235Z","endTime":"2024-02-26T13:38:18.895Z","subEntries":null}]}]}
``` ```

View file

@ -8,5 +8,6 @@
"0.1.5": "0.15.0", "0.1.5": "0.15.0",
"0.1.6": "0.15.0", "0.1.6": "0.15.0",
"0.1.7": "1.2.8", "0.1.7": "1.2.8",
"0.1.8": "1.3.0" "0.1.8": "1.3.0",
"0.2.0": "1.3.0"
} }