(opts: CreateFileFindOptions)
| 104 | } |
| 105 | |
| 106 | export function createFileFind(opts: CreateFileFindOptions) { |
| 107 | let input: HTMLInputElement | undefined |
| 108 | let overlayFrame: number | undefined |
| 109 | let mode: "highlights" | "overlay" = "overlay" |
| 110 | let hits: Range[] = [] |
| 111 | const [overlayScroll, setOverlayScroll] = createSignal<HTMLElement[]>([]) |
| 112 | |
| 113 | const [state, setState] = createStore({ |
| 114 | open: false, |
| 115 | query: "", |
| 116 | index: 0, |
| 117 | count: 0, |
| 118 | pos: { top: 8, right: 8 }, |
| 119 | }) |
| 120 | const open = () => state.open |
| 121 | const query = () => state.query |
| 122 | const index = () => state.index |
| 123 | const count = () => state.count |
| 124 | const pos = () => state.pos |
| 125 | |
| 126 | const clearOverlayScroll = () => { |
| 127 | setOverlayScroll([]) |
| 128 | } |
| 129 | |
| 130 | const clearOverlay = () => { |
| 131 | const el = opts.overlay() |
| 132 | if (!el) return |
| 133 | if (overlayFrame !== undefined) { |
| 134 | cancelAnimationFrame(overlayFrame) |
| 135 | overlayFrame = undefined |
| 136 | } |
| 137 | el.innerHTML = "" |
| 138 | } |
| 139 | |
| 140 | const renderOverlay = () => { |
| 141 | if (mode !== "overlay") { |
| 142 | clearOverlay() |
| 143 | return |
| 144 | } |
| 145 | |
| 146 | const wrapper = opts.wrapper() |
| 147 | const overlay = opts.overlay() |
| 148 | if (!wrapper || !overlay) return |
| 149 | |
| 150 | clearOverlay() |
| 151 | if (hits.length === 0) return |
| 152 | |
| 153 | const base = wrapper.getBoundingClientRect() |
| 154 | const currentIndex = index() |
| 155 | const frag = document.createDocumentFragment() |
| 156 | |
| 157 | for (let i = 0; i < hits.length; i++) { |
| 158 | const range = hits[i] |
| 159 | const active = i === currentIndex |
| 160 | for (const rect of Array.from(range.getClientRects())) { |
| 161 | if (!rect.width || !rect.height) continue |
| 162 | |
| 163 | const mark = document.createElement("div") |
no test coverage detected