UNPKG

nanosplash

Version:

The tiny loading screen for web artisans

155 lines (140 loc) 4.53 kB
import style from '@/style/ns.css?inline' import type { INanosplash } from '@/interfaces/INanosplash.ts' import type { Func } from '@/types/generic/function.ts' import type { INSElement } from '@/interfaces/INSElement.ts' import { all, bod, div, doc, first, parseHtml, parseRef, toArray, } from '@/utils/dom-utils.ts' import { ClassNames, Selectors } from '@/constants/ns.ts' import type { HTMLString } from '@/types/semantic/string.ts' import type { ElementRef } from '@/types/dom.ts' import type { int } from '@/types/semantic/number.ts' import { version } from '@/../package.json' export const useNs: Func<INanosplash> = (): INanosplash => { /** * Retrieve all NS elements from the DOM */ const getAllNs: Func<INSElement[]> = () => all(doc(), Selectors.ns) as INSElement[] /** * Create an NS element */ const makeNs: Func<INSElement> = () => { const circle: HTMLString = '<circle class=path cx=25 cy=25 r=20 fill=none />' const svg = parseHtml(`<svg viewBox="0 0 50 50">${circle}</svg>`) as Element const node = div( ClassNames.ns, div(ClassNames.nsText), div(ClassNames.nsSpinner, svg) ) as INSElement node.nsId = Date.now() return node } /** * Get all NS elements from DOM in a FIFO sequence */ const nsQueue: Func<INSElement[]> = () => getAllNs().sort((a, b) => a.nsId - b.nsId) /** * Peek at the first NS element in the queue */ const peekNsQueue: Func<INSElement | null> = () => nsQueue()[0] ?? null /** * Set the text of the NS element. * @param ns Nanosplash element. * @param text The text to display adjacent to the spinner. */ const setNsText = (ns: INSElement, text?: string): void => { first(ns, Selectors.nsText)?.remove() if (!text) return const newNsText: Element = div(ClassNames.nsText, text) ns.insertBefore(newNsText, ns.firstChild) } /** * Set the parent element of the NS element. * @param ns Nanosplash element. * @param parent An element to be a parent of the Nanosplash element. */ const setNsParent = (ns: INSElement, parent: Element): void => { const child = parent.firstElementChild if (child) parent.insertBefore(ns, child) parent.append(ns) parent.classList.add(ClassNames.nsHost) } const getNsInside = (parent: Element): INSElement | undefined => { const children = toArray(parent.children) as INSElement[] return children.find((v: Element): boolean => v.classList.contains(ClassNames.ns) ) } /** * Show the Nanosplash in the window * @param text The text that will appear adjacent to the spinner. Enter false to only show the spinner. * @param inside Element or selector to an element wherein the Nanosplash element should reside. */ const show = (text?: string, inside?: ElementRef): int | null => { const parent: Element = inside ? (parseRef(inside) ?? bod()) : bod() let ns: INSElement const recycled = getNsInside(parent) if (recycled) { ns = recycled as INSElement } else { ns = makeNs() setNsParent(ns, parent) } setNsText(ns, text ?? '') if (parent === bod()) { const top: string = scrollY + 'px' bod().style.setProperty('--ns-top', top) } return ns.nsId } /** * Remove a Nanosplash element from the DOM * @param ns The Nanosplash element */ const removeNs = (ns?: Element | INSElement | null): void => { ns?.parentElement?.classList.remove(ClassNames.nsHost) ns?.remove() } /** * Find a Nanosplash element with a given ID * @param id The Nanosplash ID */ const findNs = (id: int): INSElement | null => getAllNs().find(x => x.nsId === id) ?? null /** * Hide agnostically in FIFO order, specifically by ID, or all, by the asterisk symbol. * The latter will delete all Nanosplash elements in the DOM. * @param id Optional ID of a Nanosplash element or '*'. */ const hide = (id?: int | '*'): void => { const selectAll = id === '*' if (selectAll) getAllNs().forEach(removeNs) else removeNs(typeof id === 'number' ? findNs(id) : peekNsQueue()) } /** * Remove old Nanosplash styles and insert new ones. */ const injectStyle = () => { first(doc(), '#ns')?.remove() const styleElement: HTMLStyleElement = parseHtml( `<style id="ns">${style}</style>` ) bod().append(styleElement) } injectStyle() // Run everytime the hook is invoked. return { show, hide, version, } }