mirror of
https://github.com/Ellpeck/ObsidianSimpleTimeTracker.git
synced 2024-11-27 19:48:34 +01:00
Compare commits
7 commits
92b2a287aa
...
48d576aaa2
Author | SHA1 | Date | |
---|---|---|---|
48d576aaa2 | |||
701be28601 | |||
1d90a17e69 | |||
7633ac8144 | |||
7e108a85cb | |||
5bd9fefe60 | |||
3469232d3b |
10 changed files with 87 additions and 45 deletions
|
@ -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
4
package-lock.json
generated
|
@ -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",
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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.")
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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}]}]}
|
||||||
```
|
```
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue