mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
refactoring of highlight list
This commit is contained in:
parent
eff3e1df85
commit
c177aaa901
@ -44,7 +44,7 @@ import BacklinksWidget from "../widgets/floating_buttons/zpetne_odkazy.js";
|
||||
import SharedInfoWidget from "../widgets/shared_info.js";
|
||||
import FindWidget from "../widgets/find.js";
|
||||
import TocWidget from "../widgets/toc.js";
|
||||
import HighlightedTextWidget from "../widgets/highlighted_text.js";
|
||||
import HighlightsListWidget from "../widgets/highlights_list.js";
|
||||
import BulkActionsDialog from "../widgets/dialogs/bulk_actions.js";
|
||||
import AboutDialog from "../widgets/dialogs/about.js";
|
||||
import HelpDialog from "../widgets/dialogs/help.js";
|
||||
@ -185,7 +185,7 @@ export default class DesktopLayout {
|
||||
)
|
||||
.child(new RightPaneContainer()
|
||||
.child(new TocWidget())
|
||||
.child(new HighlightedTextWidget())
|
||||
.child(new HighlightsListWidget())
|
||||
.child(...this.customWidgets.get('right-pane'))
|
||||
)
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Widget: Show highlighted text in the right pane
|
||||
*
|
||||
* By design there's no support for nonsensical or malformed constructs:
|
||||
* By design, there's no support for nonsensical or malformed constructs:
|
||||
* - For example, if there is a formula in the middle of the highlighted text, the two ends of the formula will be regarded as two entries
|
||||
*/
|
||||
|
||||
@ -10,20 +10,20 @@ import RightPanelWidget from "./right_panel_widget.js";
|
||||
import options from "../services/options.js";
|
||||
import OnClickButtonWidget from "./buttons/onclick_button.js";
|
||||
|
||||
const TPL = `<div class="highlighted-text-widget">
|
||||
const TPL = `<div class="highlists-list-widget">
|
||||
<style>
|
||||
.highlighted-text-widget {
|
||||
.highlists-list-widget {
|
||||
padding: 10px;
|
||||
contain: none;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.highlighted-text > ol {
|
||||
.highlists-list > ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.highlighted-text li {
|
||||
.highlists-list li {
|
||||
cursor: pointer;
|
||||
margin-bottom: 3px;
|
||||
text-align: justify;
|
||||
@ -32,21 +32,21 @@ const TPL = `<div class="highlighted-text-widget">
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
.highlighted-text li:hover {
|
||||
.highlists-list li:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close-highlighted-text {
|
||||
.close-highlists-list {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<span class="highlighted-text"></span>
|
||||
<span class="highlists-list"></span>
|
||||
</div>`;
|
||||
|
||||
export default class HighlightedTextWidget extends RightPanelWidget {
|
||||
export default class HighlightsListWidget extends RightPanelWidget {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@ -67,38 +67,38 @@ export default class HighlightedTextWidget extends RightPanelWidget {
|
||||
|
||||
async doRenderBody() {
|
||||
this.$body.empty().append($(TPL));
|
||||
this.$hlt = this.$body.find('.highlighted-text');
|
||||
this.$body.find('.highlighted-text-widget').append(this.closeHltButton.render());
|
||||
this.$highlightsList = this.$body.find('.highlists-list');
|
||||
this.$body.find('.highlists-list-widget').append(this.closeHltButton.render());
|
||||
}
|
||||
|
||||
async refreshWithNote(note) {
|
||||
/*The reason for adding highlightedTextPreviousVisible is to record whether the previous state of the highlightedText is hidden or displayed,
|
||||
* and then let it be displayed/hidden at the initial time.
|
||||
* If there is no such value, when the right panel needs to display toc but not highlighttext, every time the note content is changed,
|
||||
* highlighttext Widget will appear and then close immediately, because getHlt function will consume time*/
|
||||
if (this.noteContext.viewScope.highlightedTextPreviousVisible == true) {
|
||||
/* The reason for adding highlightedTextPreviousVisible is to record whether the previous state
|
||||
of the highlightedText is hidden or displayed, and then let it be displayed/hidden at the initial time.
|
||||
If there is no such value, when the right panel needs to display toc but not highlighttext,
|
||||
every time the note content is changed, highlighttext Widget will appear and then close immediately,
|
||||
because getHlt function will consume time */
|
||||
if (this.noteContext.viewScope.highlightedTextPreviousVisible) {
|
||||
this.toggleInt(true);
|
||||
} else {
|
||||
this.toggleInt(false);
|
||||
}
|
||||
const hltLabel = note.getLabel('hideHighlightWidget');
|
||||
|
||||
const optionsHlt = JSON.parse(options.get('highlightedText'));
|
||||
|
||||
if (hltLabel?.value == "" || hltLabel?.value === "true" || optionsHlt == "") {
|
||||
if (note.isLabelTruthy('hideHighlightWidget') || !optionsHlt) {
|
||||
this.toggleInt(false);
|
||||
this.triggerCommand("reEvaluateRightPaneVisibility");
|
||||
return;
|
||||
}
|
||||
|
||||
let $hlt = "", hltLiCount = -1;
|
||||
let $highlightsList = "", hltLiCount = -1;
|
||||
// Check for type text unconditionally in case alwaysShowWidget is set
|
||||
if (this.note.type === 'text') {
|
||||
const { content } = await note.getNoteComplement();
|
||||
({ $hlt, hltLiCount } = await this.getHlt(content, optionsHlt));
|
||||
const {content} = await note.getNoteComplement();
|
||||
({$highlightsList, hltLiCount} = this.getHighlightList(content, optionsHlt));
|
||||
}
|
||||
this.$hlt.html($hlt);
|
||||
if ([undefined, "false"].includes(hltLabel?.value) && hltLiCount > 0) {
|
||||
this.$highlightsList.empty().append($highlightsList);
|
||||
if (hltLiCount > 0) {
|
||||
this.toggleInt(true);
|
||||
this.noteContext.viewScope.highlightedTextPreviousVisible = true;
|
||||
} else {
|
||||
@ -109,12 +109,9 @@ export default class HighlightedTextWidget extends RightPanelWidget {
|
||||
this.triggerCommand("reEvaluateRightPaneVisibility");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a table of helight text.
|
||||
*/
|
||||
getHlt(html, optionsHlt) {
|
||||
getHighlightList(content, optionsHlt) {
|
||||
// matches a span containing background-color
|
||||
const regex1 = /<span[^>]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
|
||||
const regex1 = /<span[^>]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
|
||||
// matches a span containing color
|
||||
const regex2 = /<span[^>]*style\s*=\s*[^>]*[^-]color:[^>]*?>[\s\S]*?<\/span>/gi;
|
||||
// match italics
|
||||
@ -125,97 +122,103 @@ export default class HighlightedTextWidget extends RightPanelWidget {
|
||||
const regex5 = /<u>[\s\S]*?<\/u>/g;
|
||||
// Possible values in optionsHlt: '["bold","italic","underline","color","bgColor"]'
|
||||
// element priority: span>i>strong>u
|
||||
let findSubStr="", combinedRegexStr = "";
|
||||
if (optionsHlt.indexOf("bgColor") >= 0){
|
||||
findSubStr+=`,span[style*="background-color"]`;
|
||||
combinedRegexStr+=`|${regex1.source}`;
|
||||
let findSubStr = "", combinedRegexStr = "";
|
||||
if (optionsHlt.includes("bgColor")) {
|
||||
findSubStr += `,span[style*="background-color"]`;
|
||||
combinedRegexStr += `|${regex1.source}`;
|
||||
}
|
||||
if (optionsHlt.indexOf("color") >= 0){
|
||||
findSubStr+=`,span[style*="color"]`;
|
||||
combinedRegexStr+=`|${regex2.source}`;
|
||||
if (optionsHlt.includes("color")) {
|
||||
findSubStr += `,span[style*="color"]`;
|
||||
combinedRegexStr += `|${regex2.source}`;
|
||||
}
|
||||
if (optionsHlt.indexOf("italic") >= 0){
|
||||
findSubStr+=`,i`;
|
||||
combinedRegexStr+=`|${regex3.source}`;
|
||||
if (optionsHlt.includes("italic")) {
|
||||
findSubStr += `,i`;
|
||||
combinedRegexStr += `|${regex3.source}`;
|
||||
}
|
||||
if (optionsHlt.indexOf("bold") >= 0){
|
||||
findSubStr+=`,strong`;
|
||||
combinedRegexStr+=`|${regex4.source}`;
|
||||
if (optionsHlt.indexOf("bold")) {
|
||||
findSubStr += `,strong`;
|
||||
combinedRegexStr += `|${regex4.source}`;
|
||||
}
|
||||
if (optionsHlt.indexOf("underline") >= 0){
|
||||
findSubStr+=`,u`;
|
||||
combinedRegexStr+=`|${regex5.source}`;
|
||||
if (optionsHlt.includes("underline")) {
|
||||
findSubStr += `,u`;
|
||||
combinedRegexStr += `|${regex5.source}`;
|
||||
}
|
||||
|
||||
findSubStr = findSubStr.substring(1)
|
||||
combinedRegexStr = `(` + combinedRegexStr.substring(1) + `)`;
|
||||
const combinedRegex = new RegExp(combinedRegexStr, 'gi');
|
||||
let $hlt = $("<ol>");
|
||||
const $highlightsList = $("<ol>");
|
||||
let prevEndIndex = -1, hltLiCount = 0;
|
||||
for (let match = null, hltIndex=0; ((match = combinedRegex.exec(html)) !== null); hltIndex++) {
|
||||
var subHtml = match[0];
|
||||
for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(content)) !== null); hltIndex++) {
|
||||
const subHtml = match[0];
|
||||
const startIndex = match.index;
|
||||
const endIndex = combinedRegex.lastIndex;
|
||||
if (prevEndIndex != -1 && startIndex === prevEndIndex) {
|
||||
//If the previous element is connected to this element in HTML, then concatenate them into one.
|
||||
$hlt.children().last().append(subHtml);
|
||||
if (prevEndIndex !== -1 && startIndex === prevEndIndex) {
|
||||
// If the previous element is connected to this element in HTML, then concatenate them into one.
|
||||
$highlightsList.children().last().append(subHtml);
|
||||
} else {
|
||||
//hide li if its text content is empty
|
||||
if ([...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim() != ""){
|
||||
var $li = $('<li>');
|
||||
$li.html(subHtml);
|
||||
$li.on("click", () => this.jumpToHlt(findSubStr,hltIndex));
|
||||
$hlt.append($li);
|
||||
// TODO: can't be done with $(subHtml).text()?
|
||||
const hasText = [...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim();
|
||||
|
||||
if (hasText) {
|
||||
$highlightsList.append(
|
||||
$('<li>')
|
||||
.html(subHtml)
|
||||
.on("click", () => this.jumpToHighlightedText(findSubStr, hltIndex))
|
||||
);
|
||||
|
||||
hltLiCount++;
|
||||
}else{
|
||||
continue
|
||||
} else {
|
||||
// hide li if its text content is empty
|
||||
continue;
|
||||
}
|
||||
}
|
||||
prevEndIndex = endIndex;
|
||||
}
|
||||
return {
|
||||
$hlt,
|
||||
$highlightsList,
|
||||
hltLiCount
|
||||
};
|
||||
}
|
||||
async jumpToHlt(findSubStr,hltIndex) {
|
||||
|
||||
async jumpToHighlightedText(findSubStr, itemIndex) {
|
||||
const isReadOnly = await this.noteContext.isReadOnly();
|
||||
let targetElement;
|
||||
if (isReadOnly) {
|
||||
const $container = await this.noteContext.getContentElement();
|
||||
targetElement=$container.find(findSubStr).filter(function() {
|
||||
if (findSubStr.indexOf("color")>=0 && findSubStr.indexOf("background-color")<0){
|
||||
targetElement = $container.find(findSubStr).filter(function () {
|
||||
if (findSubStr.indexOf("color") >= 0 && findSubStr.indexOf("background-color") < 0) {
|
||||
let color = this.style.color;
|
||||
return $(this).prop('tagName')=="SPAN" && color==""?false:true;
|
||||
}else{
|
||||
return !($(this).prop('tagName') === "SPAN" && color === "");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}).filter(function() {
|
||||
return $(this).parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent().parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent().parent().parent(findSubStr).length === 0;
|
||||
}
|
||||
}).filter(function () {
|
||||
return $(this).parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent().parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent().parent().parent(findSubStr).length === 0;
|
||||
})
|
||||
} else {
|
||||
const textEditor = await this.noteContext.getTextEditor();
|
||||
targetElement=$(textEditor.editing.view.domRoots.values().next().value).find(findSubStr).filter(function() {
|
||||
// When finding span[style*="color"] but not looking for span[style*="background-color"],
|
||||
targetElement = $(textEditor.editing.view.domRoots.values().next().value).find(findSubStr).filter(function () {
|
||||
// When finding span[style*="color"] but not looking for span[style*="background-color"],
|
||||
// the background-color error will be regarded as color, so it needs to be filtered
|
||||
if (findSubStr.indexOf("color")>=0 && findSubStr.indexOf("background-color")<0){
|
||||
if (findSubStr.indexOf("color") >= 0 && findSubStr.indexOf("background-color") < 0) {
|
||||
let color = this.style.color;
|
||||
return $(this).prop('tagName')=="SPAN" && color==""?false:true;
|
||||
}else{
|
||||
return !($(this).prop('tagName') === "SPAN" && color === "");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}).filter(function() {
|
||||
//Need to filter out the child elements of the element that has been found
|
||||
return $(this).parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent().parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent().parent().parent(findSubStr).length === 0;
|
||||
}
|
||||
}).filter(function () {
|
||||
// Need to filter out the child elements of the element that has been found
|
||||
return $(this).parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent().parent(findSubStr).length === 0
|
||||
&& $(this).parent().parent().parent().parent(findSubStr).length === 0;
|
||||
})
|
||||
}
|
||||
targetElement[hltIndex].scrollIntoView({
|
||||
targetElement[itemIndex].scrollIntoView({
|
||||
behavior: "smooth", block: "center"
|
||||
});
|
||||
}
|
||||
@ -226,7 +229,7 @@ export default class HighlightedTextWidget extends RightPanelWidget {
|
||||
this.triggerCommand('reEvaluateRightPaneVisibility');
|
||||
}
|
||||
|
||||
async entitiesReloadedEvent({ loadResults }) {
|
||||
async entitiesReloadedEvent({loadResults}) {
|
||||
if (loadResults.isNoteContentReloaded(this.noteId)) {
|
||||
await this.refresh();
|
||||
} else if (loadResults.getAttributes().find(attr => attr.type === 'label'
|
||||
@ -237,7 +240,6 @@ export default class HighlightedTextWidget extends RightPanelWidget {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CloseHltButton extends OnClickButtonWidget {
|
||||
constructor() {
|
||||
super();
|
||||
@ -250,6 +252,6 @@ class CloseHltButton extends OnClickButtonWidget {
|
||||
|
||||
widget.triggerCommand("closeHlt");
|
||||
})
|
||||
.class("icon-action close-highlighted-text");
|
||||
.class("icon-action close-highlists-list");
|
||||
}
|
||||
}
|
@ -4,13 +4,15 @@ const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>Highlighted Text</h4>
|
||||
|
||||
You can customize the highlighted text displayed in the right panel:<br>
|
||||
<p>You can customize the highlighted text displayed in the right panel:</p>
|
||||
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="bold"> Bold font </label>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="italic"> Italic font </label>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="underline"> Underlined font </label>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="color"> Font with color </label>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="bgColor"> Font with background color </label>
|
||||
</div>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="bold"> Bold font </label>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="italic"> Italic font </label>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="underline"> Underlined font </label>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="color"> Font with color </label>
|
||||
<label><input type="checkbox" class="highlighted-text-check" value="bgColor"> Font with background color </label>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
export default class HighlightedTextOptions extends OptionsWidget {
|
||||
@ -18,20 +20,20 @@ export default class HighlightedTextOptions extends OptionsWidget {
|
||||
this.$widget = $(TPL);
|
||||
this.$hlt = this.$widget.find("input.highlighted-text-check");
|
||||
this.$hlt.on('change', () => {
|
||||
const hltVals=this.$widget.find('input.highlighted-text-check[type="checkbox"]:checked').map(function() {
|
||||
const hltVals = this.$widget.find('input.highlighted-text-check[type="checkbox"]:checked').map(function () {
|
||||
return this.value;
|
||||
}).get();
|
||||
}).get();
|
||||
this.updateOption('highlightedText', JSON.stringify(hltVals));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async optionsLoaded(options) {
|
||||
const hltVals=JSON.parse(options.highlightedText);
|
||||
const hltVals = JSON.parse(options.highlightedText);
|
||||
this.$widget.find('input.highlighted-text-check[type="checkbox"]').each(function () {
|
||||
if ($.inArray($(this).val(), hltVals) !== -1) {
|
||||
$(this).prop("checked", true);
|
||||
$(this).prop("checked", true);
|
||||
} else {
|
||||
$(this).prop("checked", false);
|
||||
$(this).prop("checked", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user