mirror of
https://github.com/iaremarkus/wol-reference-viewer.git
synced 2026-07-22 06:14:20 +00:00
- Replace document.createElement with createDiv/createSpan/createFragment (Obsidian globals) - Replace document with activeDocument throughout for popout window compatibility - Replace setTimeout with activeWindow.setTimeout in ReferenceSidebarView - Replace inline style.top/style.left with setCssProps + CSS custom properties in ReferencePopover - Add CSS variables --ref-popover-top/left to styles.css for dynamic popover positioning - Fix while-loop assignment pattern in ReferenceParser (no-cond-assign) - Add @codemirror/view and @codemirror/state to dependencies - Remove unused builtin-modules dev dependency https://claude.ai/code/session_01NwoSBvufhvXrvTcTenDfGE
91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
import { App } from 'obsidian';
|
|
import { ReferenceData, appendHTML } from './types';
|
|
import { fetchReference } from './referenceService';
|
|
|
|
export class ReferencePopover {
|
|
private app: App;
|
|
private popoverEl: HTMLElement | null = null;
|
|
private showGeneration = 0;
|
|
private static instance: ReferencePopover | null = null;
|
|
|
|
private constructor(app: App) {
|
|
this.app = app;
|
|
}
|
|
|
|
public static getInstance(app: App): ReferencePopover {
|
|
if (!ReferencePopover.instance) {
|
|
ReferencePopover.instance = new ReferencePopover(app);
|
|
}
|
|
return ReferencePopover.instance;
|
|
}
|
|
|
|
public async show(targetEl: HTMLElement, verseRef: string) {
|
|
this.hide();
|
|
const generation = ++this.showGeneration;
|
|
|
|
this.popoverEl = activeDocument.createDiv({ cls: 'ref-popover' });
|
|
|
|
this.popoverEl.createEl('p', { text: 'Loading…', cls: 'ref-popover-loading' });
|
|
|
|
activeDocument.body.appendChild(this.popoverEl);
|
|
this.positionPopover(targetEl);
|
|
|
|
const data = await fetchReference(verseRef);
|
|
if (generation !== this.showGeneration) return; // stale, another show() ran
|
|
this.updateContent(data);
|
|
|
|
activeDocument.addEventListener('click', this.handleOutsideClick);
|
|
this.popoverEl.addEventListener('click', (e) => e.stopPropagation());
|
|
}
|
|
|
|
private updateContent(data: ReferenceData | null) {
|
|
if (!this.popoverEl) return;
|
|
|
|
this.popoverEl.empty();
|
|
|
|
if (!data || data.results.length === 0) {
|
|
this.popoverEl.createEl('p', { text: 'No results found.' });
|
|
return;
|
|
}
|
|
|
|
for (const html of data.results) {
|
|
appendHTML(this.popoverEl.createDiv({ cls: 'ref-popover-result' }), html);
|
|
}
|
|
}
|
|
|
|
private positionPopover(targetEl: HTMLElement) {
|
|
if (!this.popoverEl) return;
|
|
|
|
const targetRect = targetEl.getBoundingClientRect();
|
|
const popoverRect = this.popoverEl.getBoundingClientRect();
|
|
|
|
let top = targetRect.bottom + window.scrollY + 5;
|
|
let left = targetRect.left + window.scrollX;
|
|
|
|
if (left + popoverRect.width > window.innerWidth) {
|
|
left = window.innerWidth - popoverRect.width - 20;
|
|
}
|
|
if (top + popoverRect.height > window.innerHeight && targetRect.top > popoverRect.height) {
|
|
top = targetRect.top + window.scrollY - popoverRect.height - 5;
|
|
}
|
|
|
|
this.popoverEl.setCssProps({
|
|
'--ref-popover-top': `${top}px`,
|
|
'--ref-popover-left': `${left}px`,
|
|
});
|
|
}
|
|
|
|
public hide() {
|
|
if (this.popoverEl) {
|
|
this.popoverEl.remove();
|
|
this.popoverEl = null;
|
|
activeDocument.removeEventListener('click', this.handleOutsideClick);
|
|
}
|
|
}
|
|
|
|
private handleOutsideClick = (event: MouseEvent) => {
|
|
if (this.popoverEl && !this.popoverEl.contains(event.target as Node)) {
|
|
this.hide();
|
|
}
|
|
}
|
|
}
|