mirror of
https://github.com/vale981/tridactyl
synced 2025-03-05 09:31:41 -05:00
finding.ts: focus found links, let people click through highlights
This commit is contained in:
parent
2e4cdb9711
commit
1e18b571fb
1 changed files with 92 additions and 56 deletions
|
@ -22,30 +22,57 @@ function getFindHost() {
|
|||
return host
|
||||
}
|
||||
|
||||
function createHighlightingElement(rect) {
|
||||
const highlight = document.createElement("span")
|
||||
highlight.className = "TridactylSearchHighlight"
|
||||
highlight.style.position = "absolute"
|
||||
highlight.style.top = `${rect.top}px`
|
||||
highlight.style.left = `${rect.left}px`
|
||||
highlight.style.width = `${rect.right - rect.left}px`
|
||||
highlight.style.height = `${rect.bottom - rect.top}px`
|
||||
highlight.style.zIndex = "2147483645"
|
||||
unfocusHighlight(highlight)
|
||||
return highlight
|
||||
}
|
||||
class FindHighlight extends HTMLSpanElement {
|
||||
public top = Infinity
|
||||
|
||||
function unfocusHighlight(high) {
|
||||
high.style.background = `rgba(127,255,255,0.5)`
|
||||
}
|
||||
constructor(private rects, private node) {
|
||||
super()
|
||||
; (this as any).unfocus = () => {
|
||||
for (const node of this.children) {
|
||||
(node as HTMLElement).style.background = `rgba(127,255,255,0.5)`
|
||||
}
|
||||
}
|
||||
; (this as any).focus = () => {
|
||||
if (!DOM.isVisible(this.children[0])) {
|
||||
this.children[0].scrollIntoView()
|
||||
}
|
||||
let parentNode = this.node.parentNode
|
||||
while (parentNode && !(parentNode instanceof HTMLAnchorElement)) {
|
||||
parentNode = parentNode.parentNode
|
||||
}
|
||||
if (parentNode) {
|
||||
parentNode.focus()
|
||||
}
|
||||
for (const node of this.children) {
|
||||
(node as HTMLElement).style.background = `rgba(255,127,255,0.5)`
|
||||
}
|
||||
}
|
||||
|
||||
function focusHighlight(high) {
|
||||
if (!DOM.isVisible(high)) {
|
||||
high.scrollIntoView()
|
||||
this.style.position = "absolute"
|
||||
this.style.top = "0px";
|
||||
this.style.left = "0px";
|
||||
for (const rect of rects) {
|
||||
if (rect.top < this.top) {
|
||||
this.top = rect.top
|
||||
}
|
||||
const highlight = document.createElement("span")
|
||||
highlight.className = "TridactylFindHighlight"
|
||||
highlight.style.position = "absolute"
|
||||
highlight.style.top = `${rect.top}px`
|
||||
highlight.style.left = `${rect.left}px`
|
||||
highlight.style.width = `${rect.right - rect.left}px`
|
||||
highlight.style.height = `${rect.bottom - rect.top}px`
|
||||
highlight.style.zIndex = "2147483645"
|
||||
highlight.style.pointerEvents = "none"
|
||||
this.appendChild(highlight)
|
||||
}
|
||||
; (this as any).unfocus()
|
||||
}
|
||||
high.style.background = `rgba(255,127,255,0.5)`
|
||||
|
||||
}
|
||||
|
||||
customElements.define("find-highlight", FindHighlight, { extends: "span" })
|
||||
|
||||
// Highlights corresponding to the last search
|
||||
let lastHighlights
|
||||
// Which element of `lastSearch` was last selected
|
||||
|
@ -55,54 +82,66 @@ export async function jumpToMatch(searchQuery, reverse) {
|
|||
// First, search for the query
|
||||
const findcase = config.get("findcase")
|
||||
const sensitive = findcase === "sensitive" || (findcase === "smart" && searchQuery.match("[A-Z]"))
|
||||
state.lastSearchQuery = searchQuery
|
||||
lastHighlights = []
|
||||
const results = await browserBg.find.find(searchQuery, {
|
||||
const findPromise = await browserBg.find.find(searchQuery, {
|
||||
tabId: await activeTabId(),
|
||||
caseSensitive: sensitive,
|
||||
entireWord: false,
|
||||
includeRangeData: true,
|
||||
includeRectData: true,
|
||||
})
|
||||
// results are sorted by the order they appear in the page, we need them to
|
||||
// be sorted according to position instead
|
||||
const rectData = results
|
||||
.rectData
|
||||
.filter(data => data.rectsAndTexts.rectList.length > 0)
|
||||
.sort((a, b) => reverse
|
||||
? b.rectsAndTexts.rectList[0].top - a.rectsAndTexts.rectList[0].top
|
||||
: a.rectsAndTexts.rectList[0].top - b.rectsAndTexts.rectList[0].top)
|
||||
if (rectData.length < 1) {
|
||||
removeHighlighting()
|
||||
throw new Error("Pattern not found: " + state.lastSearchQuery)
|
||||
}
|
||||
|
||||
// Then, highlight it
|
||||
state.lastSearchQuery = searchQuery
|
||||
lastHighlights = []
|
||||
removeHighlighting()
|
||||
|
||||
// We need to grab all text nodes in order to find the corresponding element
|
||||
const walker = document.createTreeWalker(document, NodeFilter.SHOW_TEXT, null, false)
|
||||
const nodes = []
|
||||
let node
|
||||
do {
|
||||
node = walker.nextNode()
|
||||
nodes.push(node)
|
||||
} while (node)
|
||||
|
||||
const results = await findPromise
|
||||
|
||||
const host = getFindHost()
|
||||
let focused = false
|
||||
for (let i = 0; i < rectData.length; ++i) {
|
||||
const data = rectData[i]
|
||||
const highlights = []
|
||||
lastHighlights.push(highlights)
|
||||
for (const rect of data.rectsAndTexts.rectList) {
|
||||
const highlight = createHighlightingElement(rect)
|
||||
highlights.push(highlight)
|
||||
host.appendChild(highlight)
|
||||
for (let i = 0; i < results.count; ++i) {
|
||||
const data = results.rectData[i]
|
||||
if (data.rectsAndTexts.rectList.length < 1) {
|
||||
// When a result does not have any rectangles, it's not visible
|
||||
continue;
|
||||
}
|
||||
if (!focused && DOM.isVisible(highlights[0])) {
|
||||
const range = results.rangeData[i]
|
||||
const high = new FindHighlight(data.rectsAndTexts.rectList, nodes[range.startTextNodePos])
|
||||
host.appendChild(high)
|
||||
lastHighlights.push(high)
|
||||
if (!focused && DOM.isVisible(high)) {
|
||||
focused = true
|
||||
focusHighlight(highlights[0])
|
||||
selected = i
|
||||
; (high as any).focus()
|
||||
selected = lastHighlights.length - 1
|
||||
}
|
||||
}
|
||||
if (lastHighlights.length < 1) {
|
||||
throw new Error("Pattern not found: " + state.lastSearchQuery)
|
||||
}
|
||||
lastHighlights
|
||||
.sort(reverse ? (a, b) => b.top - a.top : (a, b) => a.top - b.top)
|
||||
if (!focused) {
|
||||
focusHighlight(lastHighlights[0][0])
|
||||
selected = 0
|
||||
/* tslint:disable:no-useless-cast */
|
||||
; (lastHighlights[selected] as any).focus()
|
||||
}
|
||||
}
|
||||
|
||||
function drawHighlights(highlights) {
|
||||
const host = getFindHost()
|
||||
highlights.forEach(elems => elems.forEach(elem => host.appendChild(elem)))
|
||||
highlights.forEach(elem => host.appendChild(elem))
|
||||
}
|
||||
|
||||
export function removeHighlighting() {
|
||||
const host = getFindHost();
|
||||
while (host.firstChild) host.removeChild(host.firstChild)
|
||||
}
|
||||
|
||||
export function jumpToNextMatch(n: number) {
|
||||
|
@ -116,12 +155,9 @@ export function jumpToNextMatch(n: number) {
|
|||
removeHighlighting()
|
||||
throw new Error("Pattern not found: " + state.lastSearchQuery)
|
||||
}
|
||||
unfocusHighlight(lastHighlights[selected][0])
|
||||
/* tslint:disable:no-useless-cast */
|
||||
; (lastHighlights[selected] as any).unfocus()
|
||||
selected = (selected + n + lastHighlights.length) % lastHighlights.length
|
||||
focusHighlight(lastHighlights[selected][0])
|
||||
}
|
||||
|
||||
export function removeHighlighting() {
|
||||
const host = getFindHost();
|
||||
while (host.firstChild) host.removeChild(host.firstChild)
|
||||
/* tslint:disable:no-useless-cast */
|
||||
; (lastHighlights[selected] as any).focus()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue