|
1 | 1 | import { MarkdownRenderer, Modal, Notice, request } from "obsidian"; |
2 | 2 | import ExcalidrawPlugin from "../../core/main"; |
3 | | -import { errorlog, escapeRegExp } from "../../utils/utils"; |
| 3 | +import { errorlog } from "../../utils/utils"; |
4 | 4 | import { log } from "src/utils/debugHelper"; |
| 5 | +import { ContentSearcher } from "../components/ContentSearcher"; |
5 | 6 |
|
6 | 7 | const URL = |
7 | 8 | "https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/index-new.md"; |
8 | 9 |
|
9 | 10 | export class ScriptInstallPrompt extends Modal { |
10 | 11 | private contentDiv: HTMLDivElement; |
| 12 | + |
11 | 13 | constructor(private plugin: ExcalidrawPlugin) { |
12 | 14 | super(plugin.app); |
13 | 15 | // this.titleEl.setText(t("INSTAL_MODAL_TITLE")); |
14 | 16 | } |
15 | 17 |
|
16 | 18 | async onOpen(): Promise<void> { |
17 | | - const searchBarWrapper = document.createElement("div"); |
18 | | - searchBarWrapper.classList.add('search-bar-wrapper'); |
19 | | - |
20 | | - |
21 | | - const searchBar = document.createElement("input"); |
22 | | - searchBar.type = "text"; |
23 | | - searchBar.id = "search-bar"; |
24 | | - searchBar.placeholder = "Search..."; |
25 | | - //searchBar.style.width = "calc(100% - 120px)"; // space for the buttons and hit count |
26 | | - |
27 | | - const nextButton = document.createElement("button"); |
28 | | - nextButton.textContent = "→"; |
29 | | - nextButton.onclick = () => this.navigateSearchResults("next"); |
30 | | - |
31 | | - const prevButton = document.createElement("button"); |
32 | | - prevButton.textContent = "←"; |
33 | | - prevButton.onclick = () => this.navigateSearchResults("previous"); |
34 | | - |
35 | | - const hitCount = document.createElement("span"); |
36 | | - hitCount.id = "hit-count"; |
37 | | - hitCount.classList.add('hit-count'); |
38 | | - |
39 | | - searchBarWrapper.appendChild(prevButton); |
40 | | - searchBarWrapper.appendChild(nextButton); |
41 | | - searchBarWrapper.appendChild(searchBar); |
42 | | - searchBarWrapper.appendChild(hitCount); |
43 | | - |
44 | | - this.contentEl.prepend(searchBarWrapper); |
45 | | - |
46 | | - searchBar.addEventListener("input", (e) => { |
47 | | - this.clearHighlights(); |
48 | | - const searchTerm = (e.target as HTMLInputElement).value; |
49 | | - |
50 | | - if (searchTerm && searchTerm.length > 0) { |
51 | | - this.highlightSearchTerm(searchTerm); |
52 | | - const totalHits = this.contentDiv.querySelectorAll("mark.search-highlight").length; |
53 | | - hitCount.textContent = totalHits > 0 ? `1/${totalHits}` : ""; |
54 | | - setTimeout(()=>this.navigateSearchResults("next")); |
55 | | - } else { |
56 | | - hitCount.textContent = ""; |
57 | | - } |
58 | | - }); |
59 | | - |
60 | | - |
61 | | - searchBar.addEventListener("keydown", (e) => { |
62 | | - // If Ctrl/Cmd + F is pressed, focus on search bar |
63 | | - if ((e.ctrlKey || e.metaKey) && e.key === "f") { |
64 | | - e.preventDefault(); |
65 | | - searchBar.focus(); |
66 | | - } |
67 | | - // If Enter is pressed, navigate to next result |
68 | | - else if (e.key === "Enter") { |
69 | | - e.preventDefault(); |
70 | | - this.navigateSearchResults(e.shiftKey ? "previous" : "next"); |
71 | | - } |
72 | | - }); |
73 | | - |
74 | 19 | this.contentEl.classList.add("excalidraw-scriptengine-install"); |
75 | 20 | this.contentDiv = document.createElement("div"); |
76 | 21 | this.contentEl.appendChild(this.contentDiv); |
77 | 22 |
|
| 23 | + const searcher = new ContentSearcher(this.contentDiv); |
| 24 | + this.contentEl.prepend(searcher.getSearchBarWrapper()); |
| 25 | + |
78 | 26 | this.containerEl.classList.add("excalidraw-scriptengine-install"); |
79 | 27 | try { |
80 | 28 | const source = await request({ url: URL }); |
@@ -111,99 +59,6 @@ export class ScriptInstallPrompt extends Modal { |
111 | 59 | } |
112 | 60 | } |
113 | 61 |
|
114 | | - highlightSearchTerm(searchTerm: string): void { |
115 | | - // Create a walker to traverse text nodes |
116 | | - const walker = document.createTreeWalker( |
117 | | - this.contentDiv, |
118 | | - NodeFilter.SHOW_TEXT, |
119 | | - { |
120 | | - acceptNode: (node: Text) => { |
121 | | - return node.nodeValue!.toLowerCase().includes(searchTerm.toLowerCase()) ? |
122 | | - NodeFilter.FILTER_ACCEPT : |
123 | | - NodeFilter.FILTER_REJECT; |
124 | | - } |
125 | | - } |
126 | | - ); |
127 | | - |
128 | | - const nodesToReplace: Text[] = []; |
129 | | - while (walker.nextNode()) { |
130 | | - nodesToReplace.push(walker.currentNode as Text); |
131 | | - } |
132 | | - |
133 | | - nodesToReplace.forEach(node => { |
134 | | - const nodeContent = node.nodeValue!; |
135 | | - const newNode = document.createDocumentFragment(); |
136 | | - |
137 | | - let lastIndex = 0; |
138 | | - let match; |
139 | | - const regex = new RegExp(escapeRegExp(searchTerm), 'gi'); |
140 | | - |
141 | | - // Iterate over all matches in the text node |
142 | | - while ((match = regex.exec(nodeContent)) !== null) { |
143 | | - const before = document.createTextNode(nodeContent.slice(lastIndex, match.index)); |
144 | | - const highlighted = document.createElement('mark'); |
145 | | - highlighted.className = 'search-highlight'; |
146 | | - highlighted.textContent = match[0]; |
147 | | - highlighted.classList.add('search-result'); |
148 | | - |
149 | | - newNode.appendChild(before); |
150 | | - newNode.appendChild(highlighted); |
151 | | - |
152 | | - lastIndex = regex.lastIndex; |
153 | | - } |
154 | | - newNode.appendChild(document.createTextNode(nodeContent.slice(lastIndex))); |
155 | | - node.replaceWith(newNode); |
156 | | - }); |
157 | | - } |
158 | | - |
159 | | - |
160 | | - clearHighlights(): void { |
161 | | - this.contentDiv.querySelectorAll("mark.search-highlight").forEach((el) => { |
162 | | - el.outerHTML = el.innerHTML; |
163 | | - }); |
164 | | - } |
165 | | - |
166 | | - navigateSearchResults(direction: "next" | "previous"): void { |
167 | | - const highlights: HTMLElement[] = Array.from( |
168 | | - this.contentDiv.querySelectorAll("mark.search-highlight") |
169 | | - ); |
170 | | - |
171 | | - if (highlights.length === 0) return; |
172 | | - |
173 | | - const currentActiveIndex = highlights.findIndex((highlight) => |
174 | | - highlight.classList.contains("active-highlight") |
175 | | - ); |
176 | | - |
177 | | - if (currentActiveIndex !== -1) { |
178 | | - highlights[currentActiveIndex].classList.remove("active-highlight"); |
179 | | - highlights[currentActiveIndex].style.border = "none"; |
180 | | - } |
181 | | - |
182 | | - let nextActiveIndex = 0; |
183 | | - if (direction === "next") { |
184 | | - nextActiveIndex = |
185 | | - currentActiveIndex === highlights.length - 1 |
186 | | - ? 0 |
187 | | - : currentActiveIndex + 1; |
188 | | - } else if (direction === "previous") { |
189 | | - nextActiveIndex = |
190 | | - currentActiveIndex === 0 |
191 | | - ? highlights.length - 1 |
192 | | - : currentActiveIndex - 1; |
193 | | - } |
194 | | - |
195 | | - const nextActiveHighlight = highlights[nextActiveIndex]; |
196 | | - nextActiveHighlight.classList.add("active-highlight"); |
197 | | - nextActiveHighlight.scrollIntoView({ |
198 | | - behavior: "smooth", |
199 | | - block: "nearest", |
200 | | - }); |
201 | | - |
202 | | - // Update the hit count |
203 | | - const hitCount = document.getElementById("hit-count"); |
204 | | - hitCount.textContent = `${nextActiveIndex + 1}/${highlights.length}`; |
205 | | - } |
206 | | - |
207 | 62 | onClose(): void { |
208 | 63 | this.contentEl.empty(); |
209 | 64 | } |
|
0 commit comments