Skip to main content
Module

x/nano_jsx/components/link.ts

🎯 SSR first, lightweight 1kB JSX library.
Go to Latest
File
import { Component } from '../component.ts'import { Helmet } from './helmet.ts'import { h } from '../core.ts'import { Fragment } from '../fragment.ts'
interface Props { [key: string]: any prefetch?: boolean | 'hover' | 'visible' href: string back?: boolean delay?: number}
/** * A simple Link component * Add <Link prefetch ..., to prefetch the html document * Add <Link prefetch="hover" ..., to prefetch the html document on hovering over the link element. */export class Link extends Component<Props> { prefetchOnHover() { this.elements[0].addEventListener('mouseover', () => this.addPrefetch(), { once: true }) }
prefetchOnVisible() { const observer = new IntersectionObserver( (entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { observer.disconnect() this.addPrefetch() } }) }, { threshold: [0, 1] } ) observer.observe(this.elements[0]) }
addPrefetch() { let doesAlreadyExist = false
// check if it is already on the dom const links = document.getElementsByTagName('link') for (let i = 0; i < links.length; i++) { // if it is not already on the dom, add it if (links[i].getAttribute('rel') === 'prefetch' && links[i].getAttribute('href') === this.props.href) { doesAlreadyExist = true } }
if (!doesAlreadyExist) { const prefetch = h('link', { rel: 'prefetch', href: this.props.href, as: 'document' }) as HTMLElement document.head.appendChild(prefetch) } }
didMount() { const { href, prefetch, delay = 0, back = false } = this.props
if (back) this.elements[0].addEventListener('click', (e: Event) => { e.preventDefault() const target = e.target as HTMLLinkElement if (target.href === document.referrer) window.history.back() else window.location.href = target.href })
if (delay > 0) this.elements[0].addEventListener('click', (e: Event) => { e.preventDefault() setTimeout(() => (window.location.href = href), delay) })
if (prefetch) { if (prefetch === 'hover') this.prefetchOnHover() else if (prefetch === 'visible') this.prefetchOnVisible() else this.addPrefetch() } }
render() { // separate children and prefetch from props const { children, prefetch, back, ref, ...rest } = this.props
// some warning messages if (!this.props.href) console.warn('Please add "href" to <Link>') if (children.length !== 1) console.warn('Please add ONE child to <Link> (<Link>child</Link>)')
const a = h('a', { ...rest }, ...children) as HTMLAnchorElement
// if ssr if (prefetch === true && !(typeof window !== 'undefined' && window.document)) { // <link rel="prefetch" href="/index.html" as="document"></link> const link = h('link', { rel: 'prefetch', href: this.props.href, as: 'document' }) const helmet = h(Helmet, null, link)
return h(Fragment, null, [helmet, a]) } // if not ssr else { return a } }}