mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 02:52:27 +08:00
feat(time_selector): add time_selector options widget
This commit is contained in:
parent
fbacb5bb71
commit
c0714a92d5
126
src/public/app/widgets/type_widgets/options/time_selector.ts
Normal file
126
src/public/app/widgets/type_widgets/options/time_selector.ts
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import OptionsWidget from "./options_widget.js";
|
||||||
|
import toastService from "../../../services/toast.js";
|
||||||
|
import { t } from "../../../services/i18n.js";
|
||||||
|
import type { OptionDefinitions, OptionMap } from "../../../../../services/options_interface.js";
|
||||||
|
|
||||||
|
|
||||||
|
type TimeSelectorConstructor = {
|
||||||
|
widgetId: string;
|
||||||
|
widgetLabelId: string;
|
||||||
|
optionValueId: keyof OptionDefinitions;
|
||||||
|
optionTimeScaleId: keyof OptionDefinitions;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const TPL = (options: Pick<TimeSelectorConstructor, "widgetId" | "widgetLabelId">) => `
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="${options.widgetId}">${t(options.widgetLabelId)}</label>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<input id="${options.widgetId}" class="form-control options-number-input" type="number" min="0" steps="1" required>
|
||||||
|
<select id="${options.widgetId}-time-scale" class="form-select duration-selector" required>
|
||||||
|
<option value="1">${t("duration.seconds")}</option>
|
||||||
|
<option value="60">${t("duration.minutes")}</option>
|
||||||
|
<option value="3600">${t("duration.hours")}</option>
|
||||||
|
<option value="86400">${t("duration.days")}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.duration-selector {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
</style>`;
|
||||||
|
|
||||||
|
export default class TimeSelector extends OptionsWidget {
|
||||||
|
|
||||||
|
private $timeValueInput!: JQuery<HTMLInputElement>;
|
||||||
|
private $timeScaleSelect!: JQuery<HTMLSelectElement>;
|
||||||
|
private internalTimeInSeconds!: string | number;
|
||||||
|
private widgetId: string;
|
||||||
|
private widgetLabelId: string;
|
||||||
|
private optionValueId: keyof OptionDefinitions;
|
||||||
|
private optionTimeScaleId: keyof OptionDefinitions;
|
||||||
|
|
||||||
|
constructor(options: TimeSelectorConstructor) {
|
||||||
|
super();
|
||||||
|
this.widgetId = options.widgetId;
|
||||||
|
this.widgetLabelId = options.widgetLabelId;
|
||||||
|
this.optionValueId = options.optionValueId;
|
||||||
|
this.optionTimeScaleId = options.optionTimeScaleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL({
|
||||||
|
widgetId: this.widgetId,
|
||||||
|
widgetLabelId: this.widgetLabelId
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.$timeValueInput = this.$widget.find(`#${this.widgetId}`);
|
||||||
|
this.$timeScaleSelect = this.$widget.find(`#${this.widgetId}-time-scale`);
|
||||||
|
|
||||||
|
this.$timeValueInput.on("change", () => {
|
||||||
|
const time = this.$timeValueInput.val();
|
||||||
|
const timeScale = this.$timeScaleSelect.val();
|
||||||
|
|
||||||
|
if (!this.handleTimeValidation() || typeof timeScale !== "string" || !time) return;
|
||||||
|
|
||||||
|
this.internalTimeInSeconds = this.convertTime(time, timeScale).toOption();
|
||||||
|
this.updateOption(this.optionValueId, this.internalTimeInSeconds);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$timeScaleSelect.on("change", () => {
|
||||||
|
|
||||||
|
const timeScale = this.$timeScaleSelect.val();
|
||||||
|
|
||||||
|
if (!this.handleTimeValidation() || typeof timeScale !== "string") return;
|
||||||
|
|
||||||
|
//calculate the new displayed value
|
||||||
|
const displayedTime = this.convertTime(this.internalTimeInSeconds, timeScale).toDisplay();
|
||||||
|
|
||||||
|
this.updateOption(this.optionTimeScaleId, timeScale);
|
||||||
|
this.$timeValueInput.val(displayedTime).trigger("change");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async optionsLoaded(options: OptionMap) {
|
||||||
|
this.internalTimeInSeconds = options[this.optionValueId];
|
||||||
|
const displayedTime = this.convertTime(options[this.optionValueId], options[this.optionTimeScaleId]).toDisplay();
|
||||||
|
this.$timeValueInput.val(displayedTime);
|
||||||
|
this.$timeScaleSelect.val(options[this.optionTimeScaleId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
convertTime(time: string | number, timeScale: string | number) {
|
||||||
|
|
||||||
|
const value = typeof time === "number" ? time : parseInt(time);
|
||||||
|
if (Number.isNaN(value)) {
|
||||||
|
throw new Error(`Time needs to be a valid integer, but received: ${time}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const operand = typeof timeScale === "number" ? timeScale : parseInt(timeScale);
|
||||||
|
if (Number.isNaN(operand) || operand < 1) {
|
||||||
|
throw new Error(`TimeScale needs to be a valid integer >= 1, but received: ${timeScale}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
toOption: () => Math.ceil(value * operand),
|
||||||
|
toDisplay: () => Math.ceil(value / operand),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTimeValidation() {
|
||||||
|
if (this.$timeValueInput.is(":invalid")) {
|
||||||
|
// TriliumNextTODO: i18n
|
||||||
|
toastService.showMessage("The entered time value is not a valid number.");
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user