From f7fea18b3b98539e9c5d2a7473b3b3408a01f639 Mon Sep 17 00:00:00 2001 From: Vanessa Date: Thu, 28 Nov 2024 22:59:21 +0800 Subject: [PATCH] :art: https://github.com/siyuan-note/siyuan/issues/13178 --- app/src/assets/scss/protyle/_wysiwyg.scss | 10 ++++ app/src/protyle/index.ts | 6 ++ app/src/protyle/util/destroy.ts | 4 ++ app/src/protyle/util/reload.ts | 8 ++- app/src/search/util.ts | 73 +++++++++++++++++++---- app/src/types/index.d.ts | 15 +++++ app/src/types/protyle.d.ts | 6 ++ 7 files changed, 110 insertions(+), 12 deletions(-) diff --git a/app/src/assets/scss/protyle/_wysiwyg.scss b/app/src/assets/scss/protyle/_wysiwyg.scss index 0079a33e8..d36546c4b 100644 --- a/app/src/assets/scss/protyle/_wysiwyg.scss +++ b/app/src/assets/scss/protyle/_wysiwyg.scss @@ -18,6 +18,16 @@ outline: none; } + &::highlight(search-mark) { + background-color: var(--b3-protyle-inline-mark-background); + color: var(--b3-protyle-inline-mark-color); + } + + &::highlight(search-mark-hl) { + background-color: var(--b3-theme-primary-lighter); + box-shadow: 0 0 0 .5px var(--b3-theme-on-background); + } + [data-node-id] { position: relative; diff --git a/app/src/protyle/index.ts b/app/src/protyle/index.ts index 0671653eb..e042e38ef 100644 --- a/app/src/protyle/index.ts +++ b/app/src/protyle/index.ts @@ -72,6 +72,12 @@ export class Protyle { element: id, options: mergedOptions, block: {}, + highlight: { + mark: new Highlight(), + markHL: new Highlight(), + ranges: [], + rangeIndex: 0, + } }; this.protyle.hint = new Hint(this.protyle); diff --git a/app/src/protyle/util/destroy.ts b/app/src/protyle/util/destroy.ts index a592d4e94..6c22d91df 100644 --- a/app/src/protyle/util/destroy.ts +++ b/app/src/protyle/util/destroy.ts @@ -5,6 +5,10 @@ export const destroy = (protyle: IProtyle) => { return; } hideElements(["util"], protyle); + protyle.highlight.markHL.clear(); + protyle.highlight.mark.clear(); + protyle.highlight.ranges = []; + protyle.highlight.rangeIndex = 0; protyle.observer?.disconnect(); protyle.observerLoad?.disconnect(); protyle.element.classList.remove("protyle"); diff --git a/app/src/protyle/util/reload.ts b/app/src/protyle/util/reload.ts index ff5586d44..8ac9a1cf2 100644 --- a/app/src/protyle/util/reload.ts +++ b/app/src/protyle/util/reload.ts @@ -4,6 +4,7 @@ import {getDocByScroll, saveScroll} from "../scroll/saveScroll"; import {renderBacklink} from "../wysiwyg/renderBacklink"; import {hasClosestByClassName} from "./hasClosest"; import {preventScroll} from "../scroll/preventScroll"; +import {highlightMark} from "../../search/util"; export const reloadProtyle = (protyle: IProtyle, focus: boolean, updateReadonly?: boolean) => { if (!protyle.preview.element.classList.contains("fn__none")) { @@ -56,7 +57,12 @@ export const reloadProtyle = (protyle: IProtyle, focus: boolean, updateReadonly? protyle, focus, scrollAttr: saveScroll(protyle, true) as IScrollAttr, - updateReadonly + updateReadonly, + cb () { + if (protyle.query?.key) { + highlightMark(protyle, protyle.wysiwyg.element.querySelectorAll(`span[data-type~="search-mark"]`)); + } + } }); } }; diff --git a/app/src/search/util.ts b/app/src/search/util.ts index 7fa8202f2..13eeb1139 100644 --- a/app/src/search/util.ts +++ b/app/src/search/util.ts @@ -1159,6 +1159,28 @@ const renderNextSearchMark = (options: { edit: Protyle, target: Element, }) => { + const contentRect = options.edit.protyle.contentElement.getBoundingClientRect(); + if (CSS.highlights) { + options.edit.protyle.highlight.markHL.clear(); + options.edit.protyle.highlight.mark.clear(); + options.edit.protyle.highlight.rangeIndex++; + if (options.edit.protyle.highlight.rangeIndex >= options.edit.protyle.highlight.ranges.length) { + options.edit.protyle.highlight.rangeIndex = 0; + } + let rangeTop + options.edit.protyle.highlight.ranges.forEach((item, index) => { + if (options.edit.protyle.highlight.rangeIndex === index) { + options.edit.protyle.highlight.markHL.add(item); + rangeTop = item.getBoundingClientRect().top + } else { + options.edit.protyle.highlight.mark.add(item); + } + }); + if (typeof rangeTop === "number") { + options.edit.protyle.contentElement.scrollTop = options.edit.protyle.contentElement.scrollTop + rangeTop - contentRect.top - contentRect.height / 2; + } + return; + } let matchElement; const allMatchElements = Array.from(options.edit.protyle.wysiwyg.element.querySelectorAll(`div[data-node-id="${options.id}"] span[data-type~="search-mark"]`)); allMatchElements.find((item, itemIndex) => { @@ -1173,7 +1195,6 @@ const renderNextSearchMark = (options: { } if (matchElement) { matchElement.classList.add("search-mark--hl"); - const contentRect = options.edit.protyle.contentElement.getBoundingClientRect(); options.edit.protyle.contentElement.scrollTop = options.edit.protyle.contentElement.scrollTop + matchElement.getBoundingClientRect().top - contentRect.top - contentRect.height / 2; } }; @@ -1209,18 +1230,23 @@ export const getArticle = (options: { updateReadonly: true, data: getResponse, protyle: options.edit.protyle, - action: zoomIn ? [Constants.CB_GET_ALL, Constants.CB_GET_HTML] : [Constants.CB_GET_HL, Constants.CB_GET_HTML], + action: zoomIn ? [Constants.CB_GET_ALL, Constants.CB_GET_HTML] : [Constants.CB_GET_HTML], }); - const matchElement = options.edit.protyle.wysiwyg.element.querySelector(`div[data-node-id="${options.id}"] span[data-type~="search-mark"]`); - if (matchElement) { - matchElement.classList.add("search-mark--hl"); - const contentRect = options.edit.protyle.contentElement.getBoundingClientRect(); - const matchRectTop = matchElement.getBoundingClientRect().top; // 需前置,否则代码高亮后会移除该元素 - setTimeout(() => { - // 等待 scrollCenter 定位后再滚动 - options.edit.protyle.contentElement.scrollTop = options.edit.protyle.contentElement.scrollTop + matchRectTop - contentRect.top - contentRect.height / 2; - }); + const matchElements = options.edit.protyle.wysiwyg.element.querySelectorAll(`div[data-node-id="${options.id}"] span[data-type~="search-mark"]`); + if (matchElements.length === 0) { + return; } + const contentRect = options.edit.protyle.contentElement.getBoundingClientRect(); + let matchRectTop: number + if (CSS.highlights) { + options.edit.protyle.highlight.rangeIndex = 0; + highlightMark(options.edit.protyle, matchElements); + matchRectTop = options.edit.protyle.highlight.ranges[0].getBoundingClientRect().top; + } else { + matchElements[0].classList.add("search-mark--hl"); + matchRectTop = matchElements[0].getBoundingClientRect().top; + } + options.edit.protyle.contentElement.scrollTop = options.edit.protyle.contentElement.scrollTop + matchRectTop - contentRect.top - contentRect.height / 2; }); }); }); @@ -1476,3 +1502,28 @@ ${item.tag ? `${it `); }; + +export const highlightMark = (protyle: IProtyle, matchElements: NodeListOf) => { + protyle.highlight.markHL.clear(); + protyle.highlight.markHL.clear(); + protyle.highlight.ranges = []; + matchElements.forEach((item, index) => { + const range = new Range(); + if (item.getAttribute("data-type") === "search-mark") { + const contentElement = item.firstChild + item.replaceWith(contentElement) + range.selectNodeContents(contentElement); + } else { + item.setAttribute("data-type", item.getAttribute("data-type").replace(" search-mark", "").replace("search-mark ", "")); + range.selectNodeContents(item); + } + if (index === protyle.highlight.rangeIndex) { + protyle.highlight.markHL.add(range); + } else { + protyle.highlight.mark.add(range); + } + protyle.highlight.ranges.push(range) + }) + CSS.highlights.set("search-mark", protyle.highlight.mark); + CSS.highlights.set("search-mark-hl", protyle.highlight.markHL); +} diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts index 5f3365959..d0744f68b 100644 --- a/app/src/types/index.d.ts +++ b/app/src/types/index.d.ts @@ -107,8 +107,23 @@ type TAVFilterOperator = | "Is relative to today" | "Is true" | "Is false" + declare module "blueimp-md5" +declare class Highlight { + constructor(...range: Range[]); + + add(range: Range): void + + clear(): void + + forEach(callbackfn: (value: Range, key: number) => void): void; +} + +declare namespace CSS { + const highlights: Map; +} + interface Window { echarts: { init(element: HTMLElement, theme?: string, options?: { diff --git a/app/src/types/protyle.d.ts b/app/src/types/protyle.d.ts index fdc667430..b508e1657 100644 --- a/app/src/types/protyle.d.ts +++ b/app/src/types/protyle.d.ts @@ -482,6 +482,12 @@ interface IProtyleOptions { } interface IProtyle { + highlight: { + mark: Highlight + markHL: Highlight + ranges: Range[] + rangeIndex: 0 + } getInstance: () => import("../protyle").Protyle, observerLoad?: ResizeObserver, observer?: ResizeObserver,