New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adds live page preview on hover :) #52
Changes from 3 commits
e1e5c57
9c06df6
b9ee395
c16e1ba
9c60b20
fc3a3d3
8a7183e
62d7a67
53f03d2
db31638
dcfc937
806203d
79d4dca
282f915
3129b48
e29481d
6740221
99338a9
02c9db7
cece075
eabd431
bb08eae
ea574cd
53459da
b64069e
cc7e463
13af95d
5a8d720
9afd28e
7210520
73004cf
f01d18e
caff0e7
aa798b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,24 +25,25 @@ browser.runtime.onMessage.addListener(async message => { | |
let iframeInstance: PreviewIframe | null = null | ||
|
||
const toggleIframe = (active: boolean) => { | ||
if (!iframeInstance) { | ||
if (active && !iframeInstance) { | ||
iframeInstance = new PreviewIframe() | ||
} | ||
if (active) { | ||
iframeInstance.activate() | ||
} else { | ||
} else if (iframeInstance) { | ||
iframeInstance.destroy() | ||
iframeInstance = null | ||
} | ||
} | ||
|
||
class PreviewIframe { | ||
iframeId = 'roam-toolkit-iframe-preview' | ||
iframe: HTMLIFrameElement | null = null | ||
iframe: HTMLIFrameElement | ||
popupTimeout: ReturnType<typeof setTimeout> | null = null | ||
hoveredElement: HTMLElement | null = null | ||
popper: Instance | null = null | ||
popupTimeoutDuration = 300 | ||
constructor() { | ||
this.iframe = document.createElement('iframe') | ||
} | ||
activate() { | ||
this.initPreviewIframe() | ||
} | ||
|
@@ -61,40 +62,34 @@ class PreviewIframe { | |
|
||
private removeIframe() { | ||
const isCurrentIframePresent = document.body.contains(this.iframe) | ||
if (!this.iframe || !isCurrentIframePresent) { | ||
if (!isCurrentIframePresent) { | ||
return | ||
} | ||
if (isCurrentIframePresent) { | ||
document.body.removeChild(this.iframe) | ||
} | ||
if (this.iframe) { | ||
this.iframe = null | ||
} | ||
} | ||
|
||
private getIFrameByUrl(url: string): HTMLIFrameElement | null { | ||
return document.querySelector(`iframe[src="${url}"]`) | ||
document.body.removeChild(this.iframe) | ||
} | ||
private getVisibleIframeByUrl(url: string): HTMLIFrameElement | null { | ||
const iframe = this.getIFrameByUrl(url) | ||
return iframe?.style.opacity === '1' ? iframe : null | ||
/** | ||
* HACK: needed because the instance is created thrice onload/toggle. | ||
* Remove this check to see the issue | ||
*/ | ||
private getExisitingIframe(): HTMLIFrameElement | null { | ||
return document.getElementById(this.iframeId) ? this.iframe : null | ||
} | ||
|
||
private initPreviewIframe() { | ||
const url = Navigation.getPageUrl() | ||
const existingIframe = this.getIFrameByUrl(url) | ||
const url = Navigation.getDailyNotesUrl() | ||
const existingIframe = this.getExisitingIframe() | ||
if (existingIframe) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hope we'd be getting rid of this now. but if we were not - I think the assignment would have been still required, to ensure that we operate on the appropriate underlying object There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
this.iframe = existingIframe | ||
return | ||
} | ||
this.iframe = this.setupHiddenIframe(url) | ||
this.setupHiddenIframe(url) | ||
this.addIframeToBody() | ||
this.scrollToTopHack() | ||
this.attachMouseListeners() | ||
} | ||
|
||
private addIframeToBody() { | ||
if (this.iframe) document.body.appendChild(this.iframe) | ||
document.body.appendChild(this.iframe) | ||
} | ||
|
||
private attachMouseListeners() { | ||
|
@@ -113,7 +108,7 @@ class PreviewIframe { | |
const text = this.getTargetInnerText(target) | ||
this.hoveredElement = target | ||
const url = Navigation.getPageUrlByName(text) | ||
if (this.iframe && url) { | ||
if (url) { | ||
this.prepIframeForDisplay(url) | ||
this.setTimerForPopup(target) | ||
} | ||
|
@@ -143,10 +138,8 @@ class PreviewIframe { | |
private setTimerForPopup(target: HTMLElement) { | ||
if (!this.popupTimeout) { | ||
this.popupTimeout = window.setTimeout(() => { | ||
if (this.iframe) { | ||
this.showPreview() | ||
this.makePopper(target) | ||
} | ||
this.showPreview() | ||
this.makePopper(target) | ||
}, this.popupTimeoutDuration) | ||
} | ||
} | ||
|
@@ -173,17 +166,15 @@ class PreviewIframe { | |
} | ||
|
||
private resetIframeForNextHover() { | ||
if (this.iframe) { | ||
this.scrollToTopOnMouseOut() | ||
this.iframe.style.pointerEvents = 'none' | ||
this.iframe.style.opacity = '0' | ||
this.iframe.style.height = '0' | ||
this.iframe.style.width = '0' | ||
} | ||
this.scrollToTopOnMouseOut() | ||
this.iframe.style.pointerEvents = 'none' | ||
this.iframe.style.opacity = '0' | ||
this.iframe.style.height = '0' | ||
this.iframe.style.width = '0' | ||
} | ||
|
||
private scrollToTopOnMouseOut() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can you place the scroll to top hacks close to each other and extract commonality between them? |
||
if (this.iframe?.contentDocument) { | ||
if (this.iframe.contentDocument) { | ||
// scroll to top when removed, so the next popup is not scrolled | ||
const scrollContainer = this.iframe.contentDocument.querySelector('.roam-center > div') | ||
if (scrollContainer) { | ||
|
@@ -193,20 +184,10 @@ class PreviewIframe { | |
} | ||
|
||
private showPreview() { | ||
if (this.iframe) { | ||
this.iframe.style.opacity = '1' | ||
this.iframe.style.pointerEvents = 'all' | ||
} | ||
this.iframe.style.opacity = '1' | ||
this.iframe.style.pointerEvents = 'all' | ||
} | ||
private prepIframeForDisplay(url: string) { | ||
if (!this.iframe) { | ||
return | ||
} | ||
const visibleIframe = this.getVisibleIframeByUrl(url) | ||
if (visibleIframe) { | ||
// if visible, just return the iframe | ||
return | ||
} | ||
// this pre-loads the iframe, (which is shown after a delay) | ||
this.iframe.src = url | ||
this.iframe.style.height = '500px' | ||
|
@@ -215,9 +196,6 @@ class PreviewIframe { | |
} | ||
|
||
private makePopper(target: HTMLElement) { | ||
if (!this.iframe) { | ||
return | ||
} | ||
this.popper = createPopper(target, this.iframe, { | ||
placement: 'right', | ||
modifiers: [ | ||
|
@@ -238,25 +216,22 @@ class PreviewIframe { | |
} | ||
|
||
private setupHiddenIframe = (url: string) => { | ||
let iframe = document.createElement('iframe') | ||
iframe.src = url | ||
iframe.style.position = 'absolute' | ||
iframe.style.left = '0' | ||
iframe.style.top = '0' | ||
iframe.style.opacity = '0' | ||
iframe.style.pointerEvents = 'none' | ||
iframe.style.height = '0' | ||
iframe.style.width = '0' | ||
iframe.style.border = '0' | ||
iframe.style.boxShadow = '0 0 4px 5px rgba(0, 0, 0, 0.2)' | ||
iframe.style.borderRadius = '4px' | ||
iframe.id = this.iframeId | ||
iframe = this.appendStylesToIFrameOnLoad(iframe) | ||
|
||
return iframe | ||
this.iframe.src = url | ||
this.iframe.style.position = 'absolute' | ||
this.iframe.style.left = '0' | ||
this.iframe.style.top = '0' | ||
this.iframe.style.opacity = '0' | ||
this.iframe.style.pointerEvents = 'none' | ||
this.iframe.style.height = '0' | ||
this.iframe.style.width = '0' | ||
this.iframe.style.border = '0' | ||
this.iframe.style.boxShadow = '0 0 4px 5px rgba(0, 0, 0, 0.2)' | ||
this.iframe.style.borderRadius = '4px' | ||
this.iframe.id = this.iframeId | ||
this.appendStylesToIFrameOnLoad() | ||
} | ||
|
||
private appendStylesToIFrameOnLoad = (iframe: HTMLIFrameElement) => { | ||
private appendStylesToIFrameOnLoad = () => { | ||
const styleNode = document.createElement('style') | ||
styleNode.innerHTML = ` | ||
.roam-topbar { | ||
|
@@ -272,10 +247,9 @@ class PreviewIframe { | |
display: none !important; | ||
} | ||
` | ||
iframe.onload = (event: Event) => { | ||
this.iframe.onload = (event: Event) => { | ||
;(event.target as HTMLIFrameElement).contentDocument?.body.appendChild(styleNode) | ||
} | ||
return iframe | ||
} | ||
/** | ||
* HACK: to reset scroll after adding iframe to DOM. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a nit, but can the prettier be configured to require at list 1 space between functions? 😛
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Stvad Haha, I wish 😅 It is not possible in prettier because of the way prettier works (rewriting your code). See this more more info
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh, interesting. May I suggest you configure your IDE to do so then ?😛