mirror of
https://github.com/zadam/trilium.git
synced 2025-12-08 08:24:24 +01:00
Merge branch 'develop' into renovate/csrf-csrf-4.x
This commit is contained in:
commit
fd90454eb6
@ -58,6 +58,7 @@ export interface ViewScope {
|
|||||||
* toc will appear and then close immediately, because getToc(html) function will consume time
|
* toc will appear and then close immediately, because getToc(html) function will consume time
|
||||||
*/
|
*/
|
||||||
tocPreviousVisible?: boolean;
|
tocPreviousVisible?: boolean;
|
||||||
|
tocCollapsedHeadings?: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateLinkOptions {
|
interface CreateLinkOptions {
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const TPL = /*html*/`
|
|||||||
height: 300px;
|
height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.open-full-button, .collapse-button {
|
.note-map-ribbon-widget .open-full-button, .note-map-ribbon-widget .collapse-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
bottom: 5px;
|
bottom: 5px;
|
||||||
|
|||||||
@ -29,23 +29,68 @@ const TPL = /*html*/`<div class="toc-widget">
|
|||||||
contain: none;
|
contain: none;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
padding-left:0px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc ol {
|
.toc ol {
|
||||||
padding-left: 25px;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 20px;
|
||||||
|
transition: max-height 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc > ol {
|
.toc > ol {
|
||||||
padding-left: 20px;
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc li.collapsed + ol {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc li + ol:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
left: 17px;
|
||||||
|
border-left: 1px solid var(--main-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.toc li {
|
.toc li {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
list-style: none;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 7px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toc li .collapse-button {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc li.collapsed .collapse-button {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc li .item-content {
|
||||||
|
margin-left: 28px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc li .collapse-button + .item-content {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.toc li:hover {
|
.toc li:hover {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
@ -231,6 +276,14 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
// Note heading 2 is the first level Trilium makes available to the note
|
// Note heading 2 is the first level Trilium makes available to the note
|
||||||
let curLevel = 2;
|
let curLevel = 2;
|
||||||
const $ols = [$toc];
|
const $ols = [$toc];
|
||||||
|
let $previousLi: JQuery<HTMLElement> | undefined;
|
||||||
|
|
||||||
|
if (!(this.noteContext?.viewScope?.tocCollapsedHeadings instanceof Set)) {
|
||||||
|
this.noteContext!.viewScope!.tocCollapsedHeadings = new Set<string>();
|
||||||
|
}
|
||||||
|
const tocCollapsedHeadings = this.noteContext!.viewScope!.tocCollapsedHeadings as Set<string>;
|
||||||
|
const validHeadingKeys = new Set<string>(); // Used to clean up obsolete entries in tocCollapsedHeadings
|
||||||
|
|
||||||
let headingCount = 0;
|
let headingCount = 0;
|
||||||
for (let m = null, headingIndex = 0; (m = headingTagsRegex.exec(html)) !== null; headingIndex++) {
|
for (let m = null, headingIndex = 0; (m = headingTagsRegex.exec(html)) !== null; headingIndex++) {
|
||||||
//
|
//
|
||||||
@ -244,6 +297,11 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
const $ol = $("<ol>");
|
const $ol = $("<ol>");
|
||||||
$ols[$ols.length - 1].append($ol);
|
$ols[$ols.length - 1].append($ol);
|
||||||
$ols.push($ol);
|
$ols.push($ol);
|
||||||
|
|
||||||
|
if ($previousLi) {
|
||||||
|
const headingKey = `h${newLevel}_${headingIndex}_${$previousLi?.text().trim()}`;
|
||||||
|
this.setupCollapsibleHeading($ol, $previousLi, headingKey, tocCollapsedHeadings, validHeadingKeys);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (levelDelta < 0) {
|
} else if (levelDelta < 0) {
|
||||||
// Close as many lists as curLevel - newLevel
|
// Close as many lists as curLevel - newLevel
|
||||||
@ -259,10 +317,20 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
//
|
//
|
||||||
|
|
||||||
const headingText = await this.replaceMathTextWithKatax(m[2]);
|
const headingText = await this.replaceMathTextWithKatax(m[2]);
|
||||||
const $li = $("<li>").html(headingText);
|
const $itemContent = $('<div class="item-content">').html(headingText).on("click", () => {
|
||||||
$li.on("click", () => this.jumpToHeading(headingIndex));
|
this.jumpToHeading(headingIndex);
|
||||||
|
});
|
||||||
|
const $li = $("<li>").append($itemContent);
|
||||||
$ols[$ols.length - 1].append($li);
|
$ols[$ols.length - 1].append($li);
|
||||||
headingCount = headingIndex;
|
headingCount = headingIndex;
|
||||||
|
$previousLi = $li;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up unused entries in tocCollapsedHeadings
|
||||||
|
for (const key of tocCollapsedHeadings) {
|
||||||
|
if (!validHeadingKeys.has(key)) {
|
||||||
|
tocCollapsedHeadings.delete(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$toc = this.pullLeft($toc);
|
$toc = this.pullLeft($toc);
|
||||||
@ -286,7 +354,7 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
|
|
||||||
const $first = $toc.children(":first");
|
const $first = $toc.children(":first");
|
||||||
|
|
||||||
if ($first[0].tagName !== "OL") {
|
if ($first[0].tagName.toLowerCase() !== "ol") {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,6 +388,59 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
headingElement?.scrollIntoView({ behavior: "smooth" });
|
headingElement?.scrollIntoView({ behavior: "smooth" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setupCollapsibleHeading($ol: JQuery<HTMLElement>, $previousLi: JQuery<HTMLElement>, headingKey: string, tocCollapsedHeadings: Set<string>, validHeadingKeys: Set<string>) {
|
||||||
|
if ($previousLi && $previousLi.find(".collapse-button").length === 0) {
|
||||||
|
const $collapseButton = $('<div class="collapse-button bx bx-chevron-down"></div>');
|
||||||
|
$previousLi.prepend($collapseButton);
|
||||||
|
|
||||||
|
// Restore the previous collapsed state
|
||||||
|
if (tocCollapsedHeadings?.has(headingKey)) {
|
||||||
|
$previousLi.addClass("collapsed");
|
||||||
|
validHeadingKeys.add(headingKey);
|
||||||
|
} else {
|
||||||
|
$previousLi.removeClass("collapsed");
|
||||||
|
}
|
||||||
|
|
||||||
|
$collapseButton.on("click", () => {
|
||||||
|
if ($previousLi.hasClass("animating")) return;
|
||||||
|
const willCollapse = !$previousLi.hasClass("collapsed");
|
||||||
|
$previousLi.addClass("animating");
|
||||||
|
|
||||||
|
if (willCollapse) { // Collapse
|
||||||
|
$ol.css("maxHeight", `${$ol.prop("scrollHeight")}px`);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
$ol.css("maxHeight", "0px");
|
||||||
|
$collapseButton.css("transform", "rotate(-90deg)");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
$ol.css("maxHeight", "");
|
||||||
|
$previousLi.addClass("collapsed");
|
||||||
|
$previousLi.removeClass("animating");
|
||||||
|
}, 300);
|
||||||
|
} else { // Expand
|
||||||
|
$previousLi.removeClass("collapsed");
|
||||||
|
$ol.css("maxHeight", "0px");
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
$ol.css("maxHeight", `${$ol.prop("scrollHeight")}px`);
|
||||||
|
$collapseButton.css("transform", "");
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
$ol.css("maxHeight", "");
|
||||||
|
$previousLi.removeClass("animating");
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (willCollapse) { // Store collapsed headings
|
||||||
|
tocCollapsedHeadings!.add(headingKey);
|
||||||
|
} else {
|
||||||
|
tocCollapsedHeadings!.delete(headingKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async closeTocCommand() {
|
async closeTocCommand() {
|
||||||
if (this.noteContext?.viewScope) {
|
if (this.noteContext?.viewScope) {
|
||||||
this.noteContext.viewScope.tocTemporarilyHidden = true;
|
this.noteContext.viewScope.tocTemporarilyHidden = true;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { beforeAll } from "vitest";
|
import { beforeAll } from "vitest";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// Initialize the translations manually to avoid any side effects.
|
// Initialize the translations manually to avoid any side effects.
|
||||||
@ -15,4 +16,8 @@ beforeAll(async () => {
|
|||||||
loadPath: join(__dirname, "../src/assets/translations/{{lng}}/{{ns}}.json")
|
loadPath: join(__dirname, "../src/assets/translations/{{lng}}/{{ns}}.json")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize dayjs
|
||||||
|
await import("dayjs/locale/en.js");
|
||||||
|
dayjs.locale("en");
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,11 +36,11 @@ export async function initializeTranslations() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`Could not load locale ${dayjsLocale}`, err);
|
console.warn(`Could not load locale ${dayjsLocale}`, err);
|
||||||
}
|
}
|
||||||
|
dayjs.locale(dayjsLocale);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ordinal(date: Dayjs) {
|
export function ordinal(date: Dayjs) {
|
||||||
return dayjs(date)
|
return dayjs(date)
|
||||||
.locale(dayjsLocale)
|
|
||||||
.format("Do");
|
.format("Do");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
* [Added support for opening and activating a note in a new tab using Ctrl+Shift+click on notes in the launcher pane, note tree, or note images](https://github.com/TriliumNext/Notes/pull/1854) by @SiriusXT
|
* [Added support for opening and activating a note in a new tab using Ctrl+Shift+click on notes in the launcher pane, note tree, or note images](https://github.com/TriliumNext/Notes/pull/1854) by @SiriusXT
|
||||||
* [Style and footnote improvements](https://github.com/TriliumNext/Notes/pull/1913) by @SiriusXT
|
* [Style and footnote improvements](https://github.com/TriliumNext/Notes/pull/1913) by @SiriusXT
|
||||||
* Backend log: disable some editor features in order to increase performance for large logs (syntax highlighting, folding, etc.).
|
* Backend log: disable some editor features in order to increase performance for large logs (syntax highlighting, folding, etc.).
|
||||||
|
* [Collapsible table of contents](https://github.com/TriliumNext/Notes/pull/1954) by @SriiusXT
|
||||||
|
|
||||||
## 📖 Documentation
|
## 📖 Documentation
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user