mirror of
https://github.com/zadam/trilium.git
synced 2025-11-15 09:45:52 +01:00
Merge branch 'develop' into feature/MFA
This commit is contained in:
@@ -206,7 +206,8 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
|
||||
doRender() {}
|
||||
|
||||
toggleInt(show: boolean | null | undefined) {
|
||||
this.$widget.toggleClass("hidden-int", !show);
|
||||
this.$widget.toggleClass("hidden-int", !show)
|
||||
.toggleClass("visible", !!show);
|
||||
}
|
||||
|
||||
isHiddenInt() {
|
||||
@@ -214,7 +215,8 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
|
||||
}
|
||||
|
||||
toggleExt(show: boolean) {
|
||||
this.$widget.toggleClass("hidden-ext", !show);
|
||||
this.$widget.toggleClass("hidden-ext", !show)
|
||||
.toggleClass("visible", !!show);
|
||||
}
|
||||
|
||||
isHiddenExt() {
|
||||
|
||||
@@ -13,7 +13,7 @@ export default class ShowTocWidgetButton extends OnClickButtonWidget {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.icon("bx-spreadsheet bx-rotate-180")
|
||||
this.icon("bx-tn-toc")
|
||||
.title(t("show_toc_widget_button.show_toc"))
|
||||
.titlePlacement("bottom")
|
||||
.onClick(() => {
|
||||
|
||||
@@ -3,6 +3,14 @@ import { t } from "../../services/i18n.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
import type BasicWidget from "../basic_widget.js";
|
||||
|
||||
/*
|
||||
* Note:
|
||||
*
|
||||
* For floating button widgets that require content to overflow, the has-overflow CSS class should
|
||||
* be applied to the root element of the widget. Additionally, this root element may need to
|
||||
* properly handle rounded corners, as defined by the --border-radius CSS variable.
|
||||
*/
|
||||
|
||||
const TPL = `
|
||||
<div class="floating-buttons no-print">
|
||||
<style>
|
||||
@@ -39,10 +47,18 @@ const TPL = `
|
||||
top: 70px;
|
||||
}
|
||||
|
||||
.type-canvas .floating-buttons-children > * {
|
||||
--border-radius: 0; /* Overridden by themes */
|
||||
}
|
||||
|
||||
.floating-buttons-children > *:not(.hidden-int):not(.no-content-hidden) {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.floating-buttons-children > *:not(.has-overflow) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.floating-buttons-children > button, .floating-buttons-children .floating-button {
|
||||
font-size: 150%;
|
||||
padding: 5px 10px 4px 10px;
|
||||
|
||||
@@ -11,7 +11,7 @@ const TPL = `
|
||||
|
||||
export default class PngExportButton extends NoteContextAwareWidget {
|
||||
isEnabled() {
|
||||
return super.isEnabled() && ["mermaid"].includes(this.note?.type ?? "") && this.note?.isContentAvailable() && this.noteContext?.viewScope?.viewMode === "default";
|
||||
return super.isEnabled() && ["mermaid", "mindMap"].includes(this.note?.type ?? "") && this.note?.isContentAvailable() && this.noteContext?.viewScope?.viewMode === "default";
|
||||
}
|
||||
|
||||
doRender() {
|
||||
|
||||
@@ -5,7 +5,7 @@ const TPL = `
|
||||
<button type="button"
|
||||
class="export-svg-button"
|
||||
title="${t("svg_export_button.button_title")}">
|
||||
<span class="bx bx-export"></span>
|
||||
<span class="bx bxs-file-image"></span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import froca from "../../services/froca.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="backlinks-widget">
|
||||
<div class="backlinks-widget has-overflow">
|
||||
<style>
|
||||
.backlinks-widget {
|
||||
position: relative;
|
||||
@@ -61,7 +61,7 @@ const TPL = `
|
||||
<span class="backlinks-count"></span>
|
||||
</div>
|
||||
|
||||
<div class="backlinks-items" style="display: none;"></div>
|
||||
<div class="backlinks-items dropdown-menu" style="display: none;"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -121,7 +121,8 @@ export default class BacklinksWidget extends NoteContextAwareWidget {
|
||||
}
|
||||
|
||||
toggle(show: boolean) {
|
||||
this.$widget.toggleClass("hidden-no-content", !show);
|
||||
this.$widget.toggleClass("hidden-no-content", !show)
|
||||
.toggleClass("visible", !!show);
|
||||
}
|
||||
|
||||
clearItems() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import server from "../services/server.js";
|
||||
import type FNote from "../entities/fnote.js";
|
||||
import type { EventData } from "../components/app_context.js";
|
||||
import type { Icon } from "./icon_list.js";
|
||||
import { Dropdown } from "bootstrap";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-icon-widget dropdown">
|
||||
@@ -88,6 +89,7 @@ interface IconToCountCache {
|
||||
|
||||
export default class NoteIconWidget extends NoteContextAwareWidget {
|
||||
|
||||
private dropdown!: bootstrap.Dropdown;
|
||||
private $icon!: JQuery<HTMLElement>;
|
||||
private $iconList!: JQuery<HTMLElement>;
|
||||
private $iconCategory!: JQuery<HTMLElement>;
|
||||
@@ -96,6 +98,8 @@ export default class NoteIconWidget extends NoteContextAwareWidget {
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")[0]);
|
||||
|
||||
this.$icon = this.$widget.find("button.note-icon");
|
||||
this.$iconList = this.$widget.find(".icon-list");
|
||||
this.$iconList.on("click", "span", async (e) => {
|
||||
@@ -130,6 +134,8 @@ export default class NoteIconWidget extends NoteContextAwareWidget {
|
||||
|
||||
async refreshWithNote(note: FNote) {
|
||||
this.$icon.removeClass().addClass(`${note.getIcon()} note-icon`);
|
||||
this.$icon.prop("disabled", !!(this.noteContext?.viewScope?.viewMode !== "default"));
|
||||
this.dropdown.hide();
|
||||
}
|
||||
|
||||
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||
|
||||
@@ -165,8 +165,7 @@ export default abstract class AbstractSplitTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
cleanup(): void {
|
||||
this.splitInstance?.destroy();
|
||||
this.splitInstance = undefined;
|
||||
this.#destroyResizer();
|
||||
}
|
||||
|
||||
async doRefresh(note: FNote | null | undefined) {
|
||||
@@ -189,17 +188,19 @@ export default abstract class AbstractSplitTypeWidget extends TypeWidget {
|
||||
|
||||
// Vertical vs horizontal layout
|
||||
const layoutOrientation = (!utils.isMobile() ? options.get("splitEditorOrientation") ?? "horizontal" : "vertical");
|
||||
if (this.layoutOrientation === layoutOrientation && this.isReadOnly === isReadOnly) {
|
||||
return;
|
||||
if (this.layoutOrientation !== layoutOrientation || this.isReadOnly !== isReadOnly) {
|
||||
this.$widget
|
||||
.toggleClass("split-horizontal", !isReadOnly && layoutOrientation === "horizontal")
|
||||
.toggleClass("split-vertical", !isReadOnly && layoutOrientation === "vertical")
|
||||
.toggleClass("split-read-only", isReadOnly);
|
||||
this.layoutOrientation = layoutOrientation as ("horizontal" | "vertical");
|
||||
this.isReadOnly = isReadOnly;
|
||||
this.#destroyResizer();
|
||||
}
|
||||
|
||||
this.$widget
|
||||
.toggleClass("split-horizontal", !isReadOnly && layoutOrientation === "horizontal")
|
||||
.toggleClass("split-vertical", !isReadOnly && layoutOrientation === "vertical")
|
||||
.toggleClass("split-read-only", isReadOnly);
|
||||
this.layoutOrientation = layoutOrientation as ("horizontal" | "vertical");
|
||||
this.isReadOnly = isReadOnly;
|
||||
this.#setupResizer();
|
||||
if (!this.splitInstance) {
|
||||
this.#setupResizer();
|
||||
}
|
||||
}
|
||||
|
||||
#setupResizer() {
|
||||
@@ -226,6 +227,11 @@ export default abstract class AbstractSplitTypeWidget extends TypeWidget {
|
||||
}
|
||||
}
|
||||
|
||||
#destroyResizer() {
|
||||
this.splitInstance?.destroy();
|
||||
this.splitInstance = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called upon when the split between the preview and content pane is initialized. Can be used to add additional listeners if needed.
|
||||
*/
|
||||
|
||||
@@ -276,4 +276,14 @@ export default class MindMapWidget extends TypeWidget {
|
||||
const svg = await this.renderSvg();
|
||||
utils.downloadSvg(this.note.title, svg);
|
||||
}
|
||||
|
||||
async exportPngEvent({ ntxId }: EventData<"exportPng">) {
|
||||
if (!this.isNoteContext(ntxId) || this.note?.type !== "mindMap") {
|
||||
return;
|
||||
}
|
||||
|
||||
const svg = await this.renderSvg();
|
||||
utils.downloadSvgAsPng(this.note.title, svg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,11 +61,7 @@ export default class ElectronIntegrationOptions extends OptionsWidget {
|
||||
this.$backgroundEffects.on("change", () => this.updateCheckboxOption("backgroundEffects", this.$backgroundEffects));
|
||||
|
||||
const restartAppButton = this.$widget.find(".restart-app-button");
|
||||
restartAppButton.on("click", () => {
|
||||
const app = utils.dynamicRequire("@electron/remote").app;
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
});
|
||||
restartAppButton.on("click", utils.restartDesktopApp);
|
||||
}
|
||||
|
||||
isEnabled() {
|
||||
|
||||
@@ -3,20 +3,26 @@ import server from "../../../../services/server.js";
|
||||
import utils from "../../../../services/utils.js";
|
||||
import { getAvailableLocales, t } from "../../../../services/i18n.js";
|
||||
import type { OptionMap } from "../../../../../../services/options_interface.js";
|
||||
import type { Locale } from "../../../../../../services/i18n.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("i18n.title")}</h4>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-6">
|
||||
<div class="locale-options-container">
|
||||
<div class="option-row">
|
||||
<label for="locale-select">${t("i18n.language")}</label>
|
||||
<select id="locale-select" class="locale-select form-select"></select>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<div class="option-row electron-only">
|
||||
<label for="formatting-locale-select">${t("i18n.formatting-locale")}</label>
|
||||
<select id="formatting-locale-select" class="formatting-locale-select form-select"></select>
|
||||
</div>
|
||||
|
||||
<div class="option-row">
|
||||
<label id="first-day-of-week-label">${t("i18n.first-day-of-the-week")}</label>
|
||||
<div role="group" aria-labelledby="first-day-of-week-label" style="margin-top: .33em;">
|
||||
<div role="group" aria-labelledby="first-day-of-week-label">
|
||||
<label class="tn-radio">
|
||||
<input name="first-day-of-week" type="radio" value="0" />
|
||||
${t("i18n.sunday")}
|
||||
@@ -28,13 +34,44 @@ const TPL = `
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option-row centered">
|
||||
<button class="btn btn-secondary btn-micro restart-app-button">${t("electron_integration.restart-app-button")}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.locale-options-container .option-row {
|
||||
border-bottom: 1px solid var(--main-border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
.locale-options-container .option-row > label {
|
||||
width: 40%;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.locale-options-container .option-row > select {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.locale-options-container .option-row:last-of-type {
|
||||
border-bottom: unset;
|
||||
}
|
||||
|
||||
.locale-options-container .option-row.centered {
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export default class LocalizationOptions extends OptionsWidget {
|
||||
|
||||
private $localeSelect!: JQuery<HTMLElement>;
|
||||
private $formattingLocaleSelect!: JQuery<HTMLElement>;
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
@@ -43,24 +80,44 @@ export default class LocalizationOptions extends OptionsWidget {
|
||||
this.$localeSelect.on("change", async () => {
|
||||
const newLocale = this.$localeSelect.val();
|
||||
await server.put(`options/locale/${newLocale}`);
|
||||
utils.reloadFrontendApp("locale change");
|
||||
});
|
||||
|
||||
this.$formattingLocaleSelect = this.$widget.find(".formatting-locale-select");
|
||||
this.$formattingLocaleSelect.on("change", async () => {
|
||||
const newLocale = this.$formattingLocaleSelect.val();
|
||||
await server.put(`options/formattingLocale/${newLocale}`);
|
||||
});
|
||||
|
||||
this.$widget.find(`input[name="first-day-of-week"]`).on("change", () => {
|
||||
const firstDayOfWeek = String(this.$widget.find(`input[name="first-day-of-week"]:checked`).val());
|
||||
this.updateOption("firstDayOfWeek", firstDayOfWeek);
|
||||
});
|
||||
this.$widget.find(".restart-app-button").on("click", utils.restartDesktopApp);
|
||||
}
|
||||
|
||||
async optionsLoaded(options: OptionMap) {
|
||||
const availableLocales = getAvailableLocales().filter(l => !l.contentOnly);
|
||||
this.$localeSelect.empty();
|
||||
const allLocales = getAvailableLocales();
|
||||
|
||||
for (const locale of availableLocales) {
|
||||
this.$localeSelect.append($("<option>").attr("value", locale.id).text(locale.name));
|
||||
function buildLocaleItem(locale: Locale, value: string) {
|
||||
return $("<option>")
|
||||
.attr("value", value)
|
||||
.text(locale.name)
|
||||
}
|
||||
|
||||
// Build list of UI locales.
|
||||
this.$localeSelect.empty();
|
||||
for (const locale of allLocales.filter(l => !l.contentOnly)) {
|
||||
this.$localeSelect.append(buildLocaleItem(locale, locale.id));
|
||||
}
|
||||
this.$localeSelect.val(options.locale);
|
||||
|
||||
// Build list of Electron locales.
|
||||
this.$formattingLocaleSelect.empty();
|
||||
for (const locale of allLocales.filter(l => l.electronLocale)) {
|
||||
this.$formattingLocaleSelect.append(buildLocaleItem(locale, locale.electronLocale as string));
|
||||
}
|
||||
this.$formattingLocaleSelect.val(options.formattingLocale);
|
||||
|
||||
this.$widget.find(`input[name="first-day-of-week"][value="${options.firstDayOfWeek}"]`)
|
||||
.prop("checked", "true");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user