nanosplash 4.0.19 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Nanosplash, Copyright (c) 2025 Isak Hauge
3
+ Copyright (c) 2025-2026 Isak Hauge
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
package/README.md CHANGED
@@ -5,9 +5,12 @@
5
5
  [![CI Production: Build, Test](https://github.com/isakhauge/nanosplash/actions/workflows/ci.yml/badge.svg)](https://github.com/isakhauge/nanosplash/actions/workflows/ci.yml)
6
6
  [![Coverage Status](https://coveralls.io/repos/github/isakhauge/nanosplash/badge.svg?branch=main)](https://coveralls.io/github/isakhauge/nanosplash?branch=main)
7
7
 
8
- ---
8
+ ![Nanosplash features: tiny 3kb gzipped, high performance, zero dependencies, minimalistic design, exports ES/CJS/IIFE, 3 function API](https://raw.githubusercontent.com/isakhauge/nanosplash/main/assets/feature-grid.svg)
9
9
 
10
10
  ```js
11
+ // Anti-flicker: appear only if slower than 150 ms, stay at least 400 ms
12
+ const ns = useNs({ showDelay: 150, minDuration: 400 })
13
+
11
14
  // Fullscreen
12
15
  ns.show('Loading')
13
16
 
@@ -16,18 +19,30 @@ ns.show('Loading', '#my-div')
16
19
 
17
20
  // Hide
18
21
  ns.hide()
22
+
23
+ // Run a job under a splash: shows before the job starts, hides when settled
24
+ const data = await ns.show(['Loading', () => fetchData()])
25
+
26
+ // Run labeled jobs sequentially under one splash — label updates per step,
27
+ // results come back as a typed tuple in order
28
+ const [user, posts] = await ns.show([
29
+ ['Loading user', () => fetchUser()],
30
+ ['Loading posts', () => fetchPosts()],
31
+ ])
19
32
  ```
20
33
 
34
+ Still just two functions. Accessible out of the box (`role="status"`, `aria-live="polite"`, `aria-busy`, `prefers-reduced-motion`) and themeable via `--ns-*` CSS custom properties.
35
+
21
36
  ## Installation
22
- Add to your project and get the typing and docs.
37
+
23
38
  ```bash
24
39
  npm install nanosplash
25
40
  ```
26
41
 
27
- Or add the script tag to your HTML.
28
42
  ```html
29
43
  <script src="https://unpkg.com/nanosplash/dist/iife/ns.iife.js"></script>
30
44
  ```
31
45
 
32
46
  ## Documentation
33
- [Read the full docs here](./docs.md)
47
+
48
+ [Read the full docs here](./docs.md)
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I='@keyframes nsRotate{to{transform:rotate(360deg)}}@keyframes nsDash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes nsFade{0%{opacity:0}to{opacity:1}}@keyframes nsAscend{0%{opacity:0;transform:translateY(13px)}to{opacity:1;transform:translateY(0)}}:root{--ns-top: 0}.nsh{--color: DarkSlateGray;--size: 20px;--relSize: calc(var(--size) * .9);--font: "Inter", "Helvetica", "Arial";--weight: 400;--bg: rgba(255, 255, 255, .9);--zIdx: 9999999999;--blur: blur(5px);position:relative;z-index:var(--zIdx)}.nsh:before{width:100%;height:100%;bottom:0;right:0;position:absolute;content:"";background-color:var(--bg);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit}body.nsh,body.nsh:before{width:100%;height:100%;position:fixed;top:var(--ns-top);left:0}.ns{width:100%;height:100%;bottom:0;right:0;display:flex;justify-content:center;align-items:center;position:absolute;z-index:calc(var(--zIdx) + 2);animation:nsFade 2s;gap:var(--relSize)}.nst{color:var(--color);font-size:var(--size);font-family:var(--font),sans-serif;font-weight:var(--weight);max-width:80dvw;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;text-shadow:0 0 .06rem rgba(0,0,0,.25);filter:drop-shadow(0 0 .07rem rgba(0,0,0,.25));animation:nsAscend .5s}.nss{display:block;width:var(--relSize);height:var(--relSize);animation:nsAscend .5s}.nss>svg{animation:nsRotate 2s linear infinite;position:relative;width:inherit;height:inherit;stroke-width:8}.nss .path{stroke:var(--color);stroke-linecap:round;animation:nsDash 1.5s ease-in-out infinite}',d=()=>globalThis.document,l=()=>globalThis.document.body,u=s=>Array.from(s),y=(s,o)=>u(s.querySelectorAll(o)),f=(s,o)=>y(s,o)[0]??null,N=s=>s instanceof Element?s:f(d(),s),c=(s,...o)=>{const i=d().createElement("div");return s&&i.classList.add(s),i.append(...o),i},m=s=>{const o=c();return o.innerHTML=s,o.firstChild},r={ns:"ns",nsHost:"nsh",nsText:"nst",nsSpinner:"nss"},v={ns:"."+r.ns,nsText:"."+r.nsText},A="4.0.19",E=()=>{const s=()=>y(d(),v.ns),o=()=>{const t=m('<svg viewBox="0 0 50 50"><circle class=path cx=25 cy=25 r=20 fill=none /></svg>'),n=c(r.ns,c(r.nsText),c(r.nsSpinner,t));return n.nsId=Date.now(),n},i=()=>s().sort((e,t)=>e.nsId-t.nsId),b=()=>i()[0]??null,g=(e,t)=>{if(f(e,v.nsText)?.remove(),!t)return;const n=c(r.nsText,t);e.insertBefore(n,e.firstChild)},x=(e,t)=>{const n=t.firstElementChild;n?t.insertBefore(e,n):t.append(e),t.classList.add(r.nsHost)},w=e=>u(e.children).find(n=>n.classList.contains(r.ns)),k=(e,t)=>{const n=t?N(t)??l():l();let a;const p=w(n);if(p?a=p:(a=o(),x(a,n)),g(a,e??""),n===l()){const T=scrollY+"px";l().style.setProperty("--ns-top",T)}return a.nsId},h=e=>{e&&(e.parentElement?.classList.remove(r.nsHost),e.remove())},z=e=>s().find(t=>t.nsId===e)??null,S=e=>{e==="*"?s().forEach(h):h(typeof e=="number"?z(e):b())};return(()=>{f(d(),"#ns")?.remove();const e=m(`<style id="ns">${I}</style>`);l().append(e)})(),{show:k,hide:S,version:A}};exports.useNs=E;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`@keyframes nsRotate{to{transform:rotate(360deg)}}@keyframes nsDash{0%{stroke-dasharray:1 150;stroke-dashoffset:0}50%{stroke-dasharray:90 150;stroke-dashoffset:-35px}to{stroke-dasharray:90 150;stroke-dashoffset:-124px}}@keyframes nsFade{0%{opacity:0}to{opacity:1}}@keyframes nsAscend{0%{opacity:0;transform:translateY(13px)}to{opacity:1;transform:translateY(0)}}:root{--ns-top:0}:where(:root){--ns-color:DarkSlateGray;--ns-size:20px;--ns-font:"Inter", "Helvetica", "Arial";--ns-weight:400;--ns-bg:#ffffffe6;--ns-z-index:10000000000;--ns-blur:blur(5px);--ns-show-delay:0s}.nsh{--color:var(--ns-color);--size:var(--ns-size);--relSize:calc(var(--size) * .9);--font:var(--ns-font);--weight:var(--ns-weight);--bg:var(--ns-bg);--zIdx:var(--ns-z-index);--blur:var(--ns-blur);z-index:var(--zIdx);position:relative}.nsh:before{animation:.2s linear backwards nsFade;animation-delay:var(--ns-show-delay);content:"";background-color:var(--bg);width:100%;height:100%;-webkit-backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit;position:absolute;bottom:0;right:0}body.nsh,body.nsh:before{width:100%;height:100%;top:var(--ns-top);position:fixed;left:0}.ns{width:100%;height:100%;z-index:calc(var(--zIdx) + 2);animation:2s backwards nsFade;animation-delay:var(--ns-show-delay);justify-content:center;align-items:center;gap:var(--relSize);display:flex;position:absolute;bottom:0;right:0}.nst{color:var(--color);font-size:var(--size);font-family:var(--font), sans-serif;font-weight:var(--weight);white-space:nowrap;text-overflow:ellipsis;text-shadow:0 0 .06rem #00000040;filter:drop-shadow(0 0 .07rem #00000040);max-width:80dvw;animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);overflow:hidden}.nss{width:var(--relSize);height:var(--relSize);animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);display:block}.nss>svg{width:inherit;height:inherit;stroke-width:8px;animation:2s linear infinite nsRotate;position:relative}.nss .path{stroke:var(--color);stroke-linecap:round;animation:1.5s ease-in-out infinite nsDash}@media (prefers-reduced-motion:reduce){.ns,.nst,.nss,.nsh:before{animation-duration:0s}.nss>svg{animation:6s linear infinite nsRotate}.nss .path{stroke-dasharray:90 150;stroke-dashoffset:-35px;animation:none}}`,t=()=>globalThis.document,n=()=>globalThis.document.body,r=e=>Array.from(e),i=(e,t)=>r(e.querySelectorAll(t)),a=(e,t)=>i(e,t)[0]??null,o=e=>e instanceof Element?e:a(t(),e),s=(e,...n)=>{let r=t().createElement(`div`);return e&&r.classList.add(e),r.append(...n),r},c=e=>{let t=s();return t.innerHTML=e,t.firstChild},l={ns:`ns`,nsHost:`nsh`,nsText:`nst`,nsSpinner:`nss`},u={ns:`.`+l.ns,nsHost:`.`+l.nsHost,nsText:`.`+l.nsText,nsSpinner:`.`+l.nsSpinner},d=`4.1.1`,f=1,p=e=>Array.isArray(e)&&typeof e[1]==`function`,m=m=>{let h=()=>i(t(),u.ns),g=()=>{let e=c(`<svg viewBox="0 0 50 50"><circle class=path cx=25 cy=25 r=20 fill=none /></svg>`);e.setAttribute(`aria-hidden`,`true`);let t=s(l.ns,s(l.nsText),s(l.nsSpinner,e));return t.setAttribute(`role`,`status`),t.setAttribute(`aria-live`,`polite`),t.nsId=f++,t},_=()=>h().filter(e=>e.nsHideTimer===void 0).sort((e,t)=>e.nsId-t.nsId),v=()=>_()[0]??null,y=(e,t)=>{if(a(e,u.nsText)?.remove(),!t)return;let n=s(l.nsText,t);e.insertBefore(n,e.firstChild)},b=(e,t)=>{let n=t.firstElementChild;n?t.insertBefore(e,n):t.append(e),t.classList.add(l.nsHost),t.setAttribute(`aria-busy`,`true`)},x=(e,t)=>{e.nsShownAt=performance.now(),e.nsShowDelay=m?.showDelay??0,e.nsMinDuration=m?.minDuration??0,t.style.setProperty(`--ns-show-delay`,e.nsShowDelay+`ms`)},S=e=>r(e.children).find(e=>e.classList.contains(l.ns)),C=(e,t)=>{let r=t?o(t):n();if(!r)return null;k();let i,a=S(r);if(a?(i=a,clearTimeout(i.nsHideTimer),i.nsHideTimer=void 0):(i=g(),b(i,r)),x(i,r),y(i,e??``),r===n()){let e=scrollY+`px`;n().style.setProperty(`--ns-top`,e)}return i.nsId},w=e=>{let t=e.parentElement;t&&(t.classList.remove(l.nsHost),t.removeAttribute(`aria-busy`),t.style.removeProperty(`--ns-show-delay`)),e.remove()},T=e=>{if(!e||e.nsHideTimer!==void 0)return;let t=e.nsShowDelay??0,n=performance.now()-((e.nsShownAt??0)+t),r=t>0&&n<0,i=(e.nsMinDuration??0)-n;if(!r&&i>0){e.nsHideTimer=setTimeout(()=>w(e),i);return}w(e)},E=e=>h().find(t=>t.nsId===e)??null,D=e=>{e===`*`?h().forEach(T):T(typeof e==`number`?E(e):v())},O=()=>c(`<style id="ns">${e}</style>`),k=()=>{let e=a(t(),`#ns`),n=O();if(!e){t().head.append(n);return}e.textContent!==n.textContent&&e.replaceWith(n)},A=async(e,t)=>{if(e.length===0)return[];let n=[],r=null;try{for(let[i,a]of e)r=C(i,t),n.push(await a())}finally{r!==null&&D(r)}return n};return{show:((e,t)=>p(e)?A([e],t).then(e=>e[0]):Array.isArray(e)?A(e,t):C(e,t)),hide:D,version:d}};exports.useNs=m;
package/dist/es/ns.es.js CHANGED
@@ -1,58 +1,80 @@
1
- const T = '@keyframes nsRotate{to{transform:rotate(360deg)}}@keyframes nsDash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes nsFade{0%{opacity:0}to{opacity:1}}@keyframes nsAscend{0%{opacity:0;transform:translateY(13px)}to{opacity:1;transform:translateY(0)}}:root{--ns-top: 0}.nsh{--color: DarkSlateGray;--size: 20px;--relSize: calc(var(--size) * .9);--font: "Inter", "Helvetica", "Arial";--weight: 400;--bg: rgba(255, 255, 255, .9);--zIdx: 9999999999;--blur: blur(5px);position:relative;z-index:var(--zIdx)}.nsh:before{width:100%;height:100%;bottom:0;right:0;position:absolute;content:"";background-color:var(--bg);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit}body.nsh,body.nsh:before{width:100%;height:100%;position:fixed;top:var(--ns-top);left:0}.ns{width:100%;height:100%;bottom:0;right:0;display:flex;justify-content:center;align-items:center;position:absolute;z-index:calc(var(--zIdx) + 2);animation:nsFade 2s;gap:var(--relSize)}.nst{color:var(--color);font-size:var(--size);font-family:var(--font),sans-serif;font-weight:var(--weight);max-width:80dvw;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;text-shadow:0 0 .06rem rgba(0,0,0,.25);filter:drop-shadow(0 0 .07rem rgba(0,0,0,.25));animation:nsAscend .5s}.nss{display:block;width:var(--relSize);height:var(--relSize);animation:nsAscend .5s}.nss>svg{animation:nsRotate 2s linear infinite;position:relative;width:inherit;height:inherit;stroke-width:8}.nss .path{stroke:var(--color);stroke-linecap:round;animation:nsDash 1.5s ease-in-out infinite}', d = () => globalThis.document, l = () => globalThis.document.body, y = (s) => Array.from(s), u = (s, o) => y(s.querySelectorAll(o)), f = (s, o) => u(s, o)[0] ?? null, A = (s) => s instanceof Element ? s : f(d(), s), c = (s, ...o) => {
2
- const i = d().createElement("div");
3
- return s && i.classList.add(s), i.append(...o), i;
4
- }, m = (s) => {
5
- const o = c();
6
- return o.innerHTML = s, o.firstChild;
7
- }, r = {
8
- ns: "ns",
9
- nsHost: "nsh",
10
- nsText: "nst",
11
- nsSpinner: "nss"
12
- }, v = {
13
- ns: "." + r.ns,
14
- nsText: "." + r.nsText
15
- }, N = "4.0.19", H = () => {
16
- const s = () => u(d(), v.ns), o = () => {
17
- const t = m('<svg viewBox="0 0 50 50"><circle class=path cx=25 cy=25 r=20 fill=none /></svg>'), n = c(
18
- r.ns,
19
- c(r.nsText),
20
- c(r.nsSpinner, t)
21
- );
22
- return n.nsId = Date.now(), n;
23
- }, i = () => s().sort((e, t) => e.nsId - t.nsId), b = () => i()[0] ?? null, g = (e, t) => {
24
- if (f(e, v.nsText)?.remove(), !t) return;
25
- const n = c(r.nsText, t);
26
- e.insertBefore(n, e.firstChild);
27
- }, x = (e, t) => {
28
- const n = t.firstElementChild;
29
- n ? t.insertBefore(e, n) : t.append(e), t.classList.add(r.nsHost);
30
- }, w = (e) => y(e.children).find((n) => n.classList.contains(r.ns)), k = (e, t) => {
31
- const n = t ? A(t) ?? l() : l();
32
- let a;
33
- const p = w(n);
34
- if (p ? a = p : (a = o(), x(a, n)), g(a, e ?? ""), n === l()) {
35
- const S = scrollY + "px";
36
- l().style.setProperty("--ns-top", S);
37
- }
38
- return a.nsId;
39
- }, h = (e) => {
40
- e && (e.parentElement?.classList.remove(r.nsHost), e.remove());
41
- }, z = (e) => s().find((t) => t.nsId === e) ?? null, I = (e) => {
42
- e === "*" ? s().forEach(h) : h(typeof e == "number" ? z(e) : b());
43
- };
44
- return (() => {
45
- f(d(), "#ns")?.remove();
46
- const e = m(
47
- `<style id="ns">${T}</style>`
48
- );
49
- l().append(e);
50
- })(), {
51
- show: k,
52
- hide: I,
53
- version: N
54
- };
55
- };
56
- export {
57
- H as useNs
1
+ //#region src/style/ns.css?inline
2
+ var e = () => globalThis.document, t = () => globalThis.document.body, n = (e) => Array.from(e), r = (e, t) => n(e.querySelectorAll(t)), i = (e, t) => r(e, t)[0] ?? null, a = (t) => t instanceof Element ? t : i(e(), t), o = (t, ...n) => {
3
+ let r = e().createElement("div");
4
+ return t && r.classList.add(t), r.append(...n), r;
5
+ }, s = (e) => {
6
+ let t = o();
7
+ return t.innerHTML = e, t.firstChild;
8
+ }, c = {
9
+ ns: "ns",
10
+ nsHost: "nsh",
11
+ nsText: "nst",
12
+ nsSpinner: "nss"
13
+ }, l = {
14
+ ns: "." + c.ns,
15
+ nsHost: "." + c.nsHost,
16
+ nsText: "." + c.nsText,
17
+ nsSpinner: "." + c.nsSpinner
18
+ }, u = "4.1.1", d = 1, f = (e) => Array.isArray(e) && typeof e[1] == "function", p = (p) => {
19
+ let m = () => r(e(), l.ns), h = () => {
20
+ let e = s("<svg viewBox=\"0 0 50 50\"><circle class=path cx=25 cy=25 r=20 fill=none /></svg>");
21
+ e.setAttribute("aria-hidden", "true");
22
+ let t = o(c.ns, o(c.nsText), o(c.nsSpinner, e));
23
+ return t.setAttribute("role", "status"), t.setAttribute("aria-live", "polite"), t.nsId = d++, t;
24
+ }, g = () => m().filter((e) => e.nsHideTimer === void 0).sort((e, t) => e.nsId - t.nsId), _ = () => g()[0] ?? null, v = (e, t) => {
25
+ if (i(e, l.nsText)?.remove(), !t) return;
26
+ let n = o(c.nsText, t);
27
+ e.insertBefore(n, e.firstChild);
28
+ }, y = (e, t) => {
29
+ let n = t.firstElementChild;
30
+ n ? t.insertBefore(e, n) : t.append(e), t.classList.add(c.nsHost), t.setAttribute("aria-busy", "true");
31
+ }, b = (e, t) => {
32
+ e.nsShownAt = performance.now(), e.nsShowDelay = p?.showDelay ?? 0, e.nsMinDuration = p?.minDuration ?? 0, t.style.setProperty("--ns-show-delay", e.nsShowDelay + "ms");
33
+ }, x = (e) => n(e.children).find((e) => e.classList.contains(c.ns)), S = (e, n) => {
34
+ let r = n ? a(n) : t();
35
+ if (!r) return null;
36
+ O();
37
+ let i, o = x(r);
38
+ if (o ? (i = o, clearTimeout(i.nsHideTimer), i.nsHideTimer = void 0) : (i = h(), y(i, r)), b(i, r), v(i, e ?? ""), r === t()) {
39
+ let e = scrollY + "px";
40
+ t().style.setProperty("--ns-top", e);
41
+ }
42
+ return i.nsId;
43
+ }, C = (e) => {
44
+ let t = e.parentElement;
45
+ t && (t.classList.remove(c.nsHost), t.removeAttribute("aria-busy"), t.style.removeProperty("--ns-show-delay")), e.remove();
46
+ }, w = (e) => {
47
+ if (!e || e.nsHideTimer !== void 0) return;
48
+ let t = e.nsShowDelay ?? 0, n = performance.now() - ((e.nsShownAt ?? 0) + t), r = t > 0 && n < 0, i = (e.nsMinDuration ?? 0) - n;
49
+ if (!r && i > 0) {
50
+ e.nsHideTimer = setTimeout(() => C(e), i);
51
+ return;
52
+ }
53
+ C(e);
54
+ }, T = (e) => m().find((t) => t.nsId === e) ?? null, E = (e) => {
55
+ e === "*" ? m().forEach(w) : w(typeof e == "number" ? T(e) : _());
56
+ }, D = () => s("<style id=\"ns\">@keyframes nsRotate{to{transform:rotate(360deg)}}@keyframes nsDash{0%{stroke-dasharray:1 150;stroke-dashoffset:0}50%{stroke-dasharray:90 150;stroke-dashoffset:-35px}to{stroke-dasharray:90 150;stroke-dashoffset:-124px}}@keyframes nsFade{0%{opacity:0}to{opacity:1}}@keyframes nsAscend{0%{opacity:0;transform:translateY(13px)}to{opacity:1;transform:translateY(0)}}:root{--ns-top:0}:where(:root){--ns-color:DarkSlateGray;--ns-size:20px;--ns-font:\"Inter\", \"Helvetica\", \"Arial\";--ns-weight:400;--ns-bg:#ffffffe6;--ns-z-index:10000000000;--ns-blur:blur(5px);--ns-show-delay:0s}.nsh{--color:var(--ns-color);--size:var(--ns-size);--relSize:calc(var(--size) * .9);--font:var(--ns-font);--weight:var(--ns-weight);--bg:var(--ns-bg);--zIdx:var(--ns-z-index);--blur:var(--ns-blur);z-index:var(--zIdx);position:relative}.nsh:before{animation:.2s linear backwards nsFade;animation-delay:var(--ns-show-delay);content:\"\";background-color:var(--bg);width:100%;height:100%;-webkit-backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit;position:absolute;bottom:0;right:0}body.nsh,body.nsh:before{width:100%;height:100%;top:var(--ns-top);position:fixed;left:0}.ns{width:100%;height:100%;z-index:calc(var(--zIdx) + 2);animation:2s backwards nsFade;animation-delay:var(--ns-show-delay);justify-content:center;align-items:center;gap:var(--relSize);display:flex;position:absolute;bottom:0;right:0}.nst{color:var(--color);font-size:var(--size);font-family:var(--font), sans-serif;font-weight:var(--weight);white-space:nowrap;text-overflow:ellipsis;text-shadow:0 0 .06rem #00000040;filter:drop-shadow(0 0 .07rem #00000040);max-width:80dvw;animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);overflow:hidden}.nss{width:var(--relSize);height:var(--relSize);animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);display:block}.nss>svg{width:inherit;height:inherit;stroke-width:8px;animation:2s linear infinite nsRotate;position:relative}.nss .path{stroke:var(--color);stroke-linecap:round;animation:1.5s ease-in-out infinite nsDash}@media (prefers-reduced-motion:reduce){.ns,.nst,.nss,.nsh:before{animation-duration:0s}.nss>svg{animation:6s linear infinite nsRotate}.nss .path{stroke-dasharray:90 150;stroke-dashoffset:-35px;animation:none}}</style>"), O = () => {
57
+ let t = i(e(), "#ns"), n = D();
58
+ if (!t) {
59
+ e().head.append(n);
60
+ return;
61
+ }
62
+ t.textContent !== n.textContent && t.replaceWith(n);
63
+ }, k = async (e, t) => {
64
+ if (e.length === 0) return [];
65
+ let n = [], r = null;
66
+ try {
67
+ for (let [i, a] of e) r = S(i, t), n.push(await a());
68
+ } finally {
69
+ r !== null && E(r);
70
+ }
71
+ return n;
72
+ };
73
+ return {
74
+ show: ((e, t) => f(e) ? k([e], t).then((e) => e[0]) : Array.isArray(e) ? k(e, t) : S(e, t)),
75
+ hide: E,
76
+ version: u
77
+ };
58
78
  };
79
+ //#endregion
80
+ export { p as useNs };
@@ -1 +1 @@
1
- (function(){"use strict";const b='@keyframes nsRotate{to{transform:rotate(360deg)}}@keyframes nsDash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes nsFade{0%{opacity:0}to{opacity:1}}@keyframes nsAscend{0%{opacity:0;transform:translateY(13px)}to{opacity:1;transform:translateY(0)}}:root{--ns-top: 0}.nsh{--color: DarkSlateGray;--size: 20px;--relSize: calc(var(--size) * .9);--font: "Inter", "Helvetica", "Arial";--weight: 400;--bg: rgba(255, 255, 255, .9);--zIdx: 9999999999;--blur: blur(5px);position:relative;z-index:var(--zIdx)}.nsh:before{width:100%;height:100%;bottom:0;right:0;position:absolute;content:"";background-color:var(--bg);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit}body.nsh,body.nsh:before{width:100%;height:100%;position:fixed;top:var(--ns-top);left:0}.ns{width:100%;height:100%;bottom:0;right:0;display:flex;justify-content:center;align-items:center;position:absolute;z-index:calc(var(--zIdx) + 2);animation:nsFade 2s;gap:var(--relSize)}.nst{color:var(--color);font-size:var(--size);font-family:var(--font),sans-serif;font-weight:var(--weight);max-width:80dvw;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;text-shadow:0 0 .06rem rgba(0,0,0,.25);filter:drop-shadow(0 0 .07rem rgba(0,0,0,.25));animation:nsAscend .5s}.nss{display:block;width:var(--relSize);height:var(--relSize);animation:nsAscend .5s}.nss>svg{animation:nsRotate 2s linear infinite;position:relative;width:inherit;height:inherit;stroke-width:8}.nss .path{stroke:var(--color);stroke-linecap:round;animation:nsDash 1.5s ease-in-out infinite}',d=()=>globalThis.document,i=()=>globalThis.document.body,p=s=>Array.from(s),v=(s,o)=>p(s.querySelectorAll(o)),h=(s,o)=>v(s,o)[0]??null,g=s=>s instanceof Element?s:h(d(),s),a=(s,...o)=>{const l=d().createElement("div");return s&&l.classList.add(s),l.append(...o),l},m=s=>{const o=a();return o.innerHTML=s,o.firstChild},r={ns:"ns",nsHost:"nsh",nsText:"nst",nsSpinner:"nss"},u={ns:"."+r.ns,nsText:"."+r.nsText},x="4.0.19",w=()=>{const s=()=>v(d(),u.ns),o=()=>{const t=m('<svg viewBox="0 0 50 50"><circle class=path cx=25 cy=25 r=20 fill=none /></svg>'),n=a(r.ns,a(r.nsText),a(r.nsSpinner,t));return n.nsId=Date.now(),n},l=()=>s().sort((e,t)=>e.nsId-t.nsId),k=()=>l()[0]??null,z=(e,t)=>{if(h(e,u.nsText)?.remove(),!t)return;const n=a(r.nsText,t);e.insertBefore(n,e.firstChild)},I=(e,t)=>{const n=t.firstElementChild;n?t.insertBefore(e,n):t.append(e),t.classList.add(r.nsHost)},S=e=>p(e.children).find(n=>n.classList.contains(r.ns)),T=(e,t)=>{const n=t?g(t)??i():i();let c;const y=S(n);if(y?c=y:(c=o(),I(c,n)),z(c,e??""),n===i()){const E=scrollY+"px";i().style.setProperty("--ns-top",E)}return c.nsId},f=e=>{e&&(e.parentElement?.classList.remove(r.nsHost),e.remove())},A=e=>s().find(t=>t.nsId===e)??null,N=e=>{e==="*"?s().forEach(f):f(typeof e=="number"?A(e):k())};return(()=>{h(d(),"#ns")?.remove();const e=m(`<style id="ns">${b}</style>`);i().append(e)})(),{show:T,hide:N,version:x}};window.addEventListener("load",function(){this.ns=w()})})();
1
+ (function(){var e=()=>globalThis.document,t=()=>globalThis.document.body,n=e=>Array.from(e),r=(e,t)=>n(e.querySelectorAll(t)),i=(e,t)=>r(e,t)[0]??null,a=t=>t instanceof Element?t:i(e(),t),o=(t,...n)=>{let r=e().createElement(`div`);return t&&r.classList.add(t),r.append(...n),r},s=e=>{let t=o();return t.innerHTML=e,t.firstChild},c={ns:`ns`,nsHost:`nsh`,nsText:`nst`,nsSpinner:`nss`},l={ns:`.`+c.ns,nsHost:`.`+c.nsHost,nsText:`.`+c.nsText,nsSpinner:`.`+c.nsSpinner},u=`4.1.1`,d=1,f=e=>Array.isArray(e)&&typeof e[1]==`function`,p=p=>{let m=()=>r(e(),l.ns),h=()=>{let e=s(`<svg viewBox="0 0 50 50"><circle class=path cx=25 cy=25 r=20 fill=none /></svg>`);e.setAttribute(`aria-hidden`,`true`);let t=o(c.ns,o(c.nsText),o(c.nsSpinner,e));return t.setAttribute(`role`,`status`),t.setAttribute(`aria-live`,`polite`),t.nsId=d++,t},g=()=>m().filter(e=>e.nsHideTimer===void 0).sort((e,t)=>e.nsId-t.nsId),_=()=>g()[0]??null,v=(e,t)=>{if(i(e,l.nsText)?.remove(),!t)return;let n=o(c.nsText,t);e.insertBefore(n,e.firstChild)},y=(e,t)=>{let n=t.firstElementChild;n?t.insertBefore(e,n):t.append(e),t.classList.add(c.nsHost),t.setAttribute(`aria-busy`,`true`)},b=(e,t)=>{e.nsShownAt=performance.now(),e.nsShowDelay=p?.showDelay??0,e.nsMinDuration=p?.minDuration??0,t.style.setProperty(`--ns-show-delay`,e.nsShowDelay+`ms`)},x=e=>n(e.children).find(e=>e.classList.contains(c.ns)),S=(e,n)=>{let r=n?a(n):t();if(!r)return null;O();let i,o=x(r);if(o?(i=o,clearTimeout(i.nsHideTimer),i.nsHideTimer=void 0):(i=h(),y(i,r)),b(i,r),v(i,e??``),r===t()){let e=scrollY+`px`;t().style.setProperty(`--ns-top`,e)}return i.nsId},C=e=>{let t=e.parentElement;t&&(t.classList.remove(c.nsHost),t.removeAttribute(`aria-busy`),t.style.removeProperty(`--ns-show-delay`)),e.remove()},w=e=>{if(!e||e.nsHideTimer!==void 0)return;let t=e.nsShowDelay??0,n=performance.now()-((e.nsShownAt??0)+t),r=t>0&&n<0,i=(e.nsMinDuration??0)-n;if(!r&&i>0){e.nsHideTimer=setTimeout(()=>C(e),i);return}C(e)},T=e=>m().find(t=>t.nsId===e)??null,E=e=>{e===`*`?m().forEach(w):w(typeof e==`number`?T(e):_())},D=()=>s(`<style id="ns">@keyframes nsRotate{to{transform:rotate(360deg)}}@keyframes nsDash{0%{stroke-dasharray:1 150;stroke-dashoffset:0}50%{stroke-dasharray:90 150;stroke-dashoffset:-35px}to{stroke-dasharray:90 150;stroke-dashoffset:-124px}}@keyframes nsFade{0%{opacity:0}to{opacity:1}}@keyframes nsAscend{0%{opacity:0;transform:translateY(13px)}to{opacity:1;transform:translateY(0)}}:root{--ns-top:0}:where(:root){--ns-color:DarkSlateGray;--ns-size:20px;--ns-font:"Inter", "Helvetica", "Arial";--ns-weight:400;--ns-bg:#ffffffe6;--ns-z-index:10000000000;--ns-blur:blur(5px);--ns-show-delay:0s}.nsh{--color:var(--ns-color);--size:var(--ns-size);--relSize:calc(var(--size) * .9);--font:var(--ns-font);--weight:var(--ns-weight);--bg:var(--ns-bg);--zIdx:var(--ns-z-index);--blur:var(--ns-blur);z-index:var(--zIdx);position:relative}.nsh:before{animation:.2s linear backwards nsFade;animation-delay:var(--ns-show-delay);content:"";background-color:var(--bg);width:100%;height:100%;-webkit-backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit;position:absolute;bottom:0;right:0}body.nsh,body.nsh:before{width:100%;height:100%;top:var(--ns-top);position:fixed;left:0}.ns{width:100%;height:100%;z-index:calc(var(--zIdx) + 2);animation:2s backwards nsFade;animation-delay:var(--ns-show-delay);justify-content:center;align-items:center;gap:var(--relSize);display:flex;position:absolute;bottom:0;right:0}.nst{color:var(--color);font-size:var(--size);font-family:var(--font), sans-serif;font-weight:var(--weight);white-space:nowrap;text-overflow:ellipsis;text-shadow:0 0 .06rem #00000040;filter:drop-shadow(0 0 .07rem #00000040);max-width:80dvw;animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);overflow:hidden}.nss{width:var(--relSize);height:var(--relSize);animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);display:block}.nss>svg{width:inherit;height:inherit;stroke-width:8px;animation:2s linear infinite nsRotate;position:relative}.nss .path{stroke:var(--color);stroke-linecap:round;animation:1.5s ease-in-out infinite nsDash}@media (prefers-reduced-motion:reduce){.ns,.nst,.nss,.nsh:before{animation-duration:0s}.nss>svg{animation:6s linear infinite nsRotate}.nss .path{stroke-dasharray:90 150;stroke-dashoffset:-35px;animation:none}}</style>`),O=()=>{let t=i(e(),`#ns`),n=D();if(!t){e().head.append(n);return}t.textContent!==n.textContent&&t.replaceWith(n)},k=async(e,t)=>{if(e.length===0)return[];let n=[],r=null;try{for(let[i,a]of e)r=S(i,t),n.push(await a())}finally{r!==null&&E(r)}return n};return{show:((e,t)=>f(e)?k([e],t).then(e=>e[0]):Array.isArray(e)?k(e,t):S(e,t)),hide:E,version:u}};window.addEventListener(`load`,()=>{window.ns=p()})})();
package/package.json CHANGED
@@ -1,55 +1,47 @@
1
1
  {
2
2
  "name": "nanosplash",
3
+ "version": "4.1.1",
3
4
  "private": false,
4
- "version": "4.0.19",
5
- "type": "module",
6
- "author": {
7
- "name": "Isak K. Hauge",
8
- "email": "isakhauge@icloud.com",
9
- "url": "https://no.linkedin.com/in/isakhauge"
10
- },
11
- "repository": {
12
- "type": "git",
13
- "url": "git+https://github.com/isakhauge/nanosplash.git"
14
- },
15
5
  "description": "The tiny loading screen for web artisans",
16
- "homepage": "https://github.com/isakhauge/nanosplash",
17
6
  "keywords": [
18
- "loading",
7
+ "cdn",
8
+ "es",
9
+ "esm",
10
+ "iife",
19
11
  "loader",
20
- "screen",
21
- "splash",
12
+ "loading",
22
13
  "nano",
23
14
  "nanosplash",
15
+ "screen",
24
16
  "small",
17
+ "splash",
25
18
  "tiny",
26
- "es",
27
- "esm",
28
19
  "ts",
29
20
  "typescript",
30
- "iife",
31
- "cdn",
32
21
  "vite"
33
22
  ],
34
- "scripts": {
35
- "dev": "vite",
36
- "build": "bun run build:es && bun run build:cjs && bun run build:iife",
37
- "build:es": "tsc && cross-env entry=./src/composables/ns.ts format=es vite build -m production --outDir ./dist/es",
38
- "build:cjs": "tsc && cross-env entry=./src/composables/ns.ts format=cjs vite build -m production --outDir ./dist/cjs",
39
- "build:iife": "tsc && cross-env entry=./src/iife.ts format=iife vite build -m production --outDir ./dist/iife",
40
- "preview": "vite preview",
41
- "lint": "eslint . --fix",
42
- "format": "prettier --write ./src/**/*.ts ./vite.config.ts ./vitest.config.ts",
43
- "test": "bunx vitest --coverage"
23
+ "homepage": "https://github.com/isakhauge/nanosplash",
24
+ "author": {
25
+ "name": "Isak K. Hauge",
26
+ "email": "isakhauge@icloud.com",
27
+ "url": "https://no.linkedin.com/in/isakhauge"
44
28
  },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/isakhauge/nanosplash.git"
32
+ },
33
+ "source": "./src/composables/ns.ts",
45
34
  "files": [
46
35
  "dist",
47
36
  "types",
48
37
  "README.md",
49
38
  "LICENSE"
50
39
  ],
51
- "types": "./types/global.d.ts",
40
+ "type": "module",
52
41
  "main": "./dist/es/ns.es.js",
42
+ "types": "./types/global.d.ts",
43
+ "unpkg": "./dist/iife/ns.iife.js",
44
+ "jsdelivr": "./dist/iife/ns.iife.js",
53
45
  "exports": {
54
46
  ".": {
55
47
  "types": "./types/global.d.ts",
@@ -58,27 +50,40 @@
58
50
  "default": "./dist/es/ns.es.js"
59
51
  }
60
52
  },
61
- "source": "./src/composables/ns.ts",
62
- "unpkg": "./dist/iife/ns.iife.js",
63
- "jsdelivr": "./dist/iife/ns.iife.js",
53
+ "scripts": {
54
+ "dev": "vp dev",
55
+ "build": "bun run build:es && bun run build:cjs && bun run build:iife",
56
+ "build:es": "tsc && cross-env entry=./src/composables/ns.ts format=es vp build -m production --outDir ./dist/es",
57
+ "build:cjs": "tsc && cross-env entry=./src/composables/ns.ts format=cjs vp build -m production --outDir ./dist/cjs",
58
+ "build:iife": "tsc && cross-env entry=./src/iife.ts format=iife vp build -m production --outDir ./dist/iife",
59
+ "preview": "vp preview",
60
+ "lint": "vp lint",
61
+ "format": "vp fmt",
62
+ "test": "vp test --coverage",
63
+ "check": "vp check"
64
+ },
64
65
  "devDependencies": {
65
- "@eslint/js": "^10.0.1",
66
66
  "@types/node": "^25.3.0",
67
- "@typescript-eslint/eslint-plugin": "^8.56.0",
68
- "@typescript-eslint/parser": "^8.56.0",
69
- "@vitest/coverage-v8": "4.0.18",
67
+ "@vitest/coverage-v8": "4.1.11",
70
68
  "cross-env": "^10.1.0",
71
69
  "cssnano": "^7.1.2",
72
70
  "dotenv": "^17.3.1",
73
- "eslint": "^10.0.1",
74
- "eslint-config-prettier": "^10.1.8",
75
- "globals": "^17.3.0",
76
71
  "jsdom": "^28.1.0",
77
72
  "postcss": "^8.5.6",
78
- "prettier": "3.8.1",
79
- "typescript": "^5.9.3",
80
- "typescript-eslint": "^8.56.0",
81
- "vite": "^7.3.1",
82
- "vitest": "^4.0.18"
73
+ "typescript": "7.0.2",
74
+ "vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
75
+ "vite-plus": "0.3.0",
76
+ "vitest": "4.1.11"
77
+ },
78
+ "overrides": {
79
+ "vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
80
+ "vitest": "4.1.11"
81
+ },
82
+ "devEngines": {
83
+ "packageManager": {
84
+ "name": "bun",
85
+ "version": "1.4.0",
86
+ "onFail": "warn"
87
+ }
83
88
  }
84
89
  }
package/types/global.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { INanosplash } from './interfaces/INanosplash.ts'
1
+ import type { INanosplash, NsOptions } from './interfaces/INanosplash.ts'
2
2
 
3
3
  declare global {
4
4
  interface Window {
@@ -11,9 +11,11 @@ declare global {
11
11
  *
12
12
  * **The tiny loading screen for web artisans**
13
13
  *
14
- * `useNs` is the main hook, which will inject the style into the DOM and return
15
- * an instance of the Nanosplash API.
16
- * We recommend that you add Nanosplash to the Window.
14
+ * Create the Nanosplash API. `useNs` is the sole entry point; styles inject
15
+ * lazily on the first `show()`.
16
+ * We recommend adding Nanosplash to the Window.
17
+ * @param options Anti-flicker timing applied to every splash shown through
18
+ * this instance.
17
19
  * @returns Instance of Nanosplash API
18
20
  * @example The short and easy way.
19
21
  * window.ns = useNs()
@@ -21,6 +23,8 @@ declare global {
21
23
  * window.addEventListener('load', function() {
22
24
  * this.ns = useNs()
23
25
  * })
26
+ * @example With anti-flicker timing.
27
+ * const ns = useNs({ showDelay: 150, minDuration: 400 })
24
28
  * @author Isak Hauge <https://www.linkedin.com/in/isakhauge/>
25
29
  */
26
- export const useNs: () => INanosplash
30
+ export const useNs: (options?: NsOptions) => INanosplash
@@ -1,5 +1,13 @@
1
- import type { int } from "../semantic/number";
1
+ import type { int } from '../semantic/number'
2
2
 
3
3
  export interface INSElement extends HTMLDivElement {
4
4
  nsId: int
5
+ /** `performance.now()` timestamp of the last `show()` call targeting this element. */
6
+ nsShownAt?: number
7
+ /** Milliseconds the splash stays invisible after `show()` (anti-flicker). */
8
+ nsShowDelay?: int
9
+ /** Minimum milliseconds the splash stays visible once shown (anti-flicker). */
10
+ nsMinDuration?: int
11
+ /** Pending deferred-removal timer, set while a `hide()` waits out `minDuration`. */
12
+ nsHideTimer?: ReturnType<typeof setTimeout>
5
13
  }
@@ -1,6 +1,64 @@
1
1
  import type { ElementRef } from '../dom'
2
2
  import type { int } from '../semantic/number'
3
3
 
4
+ /**
5
+ * # Options
6
+ * Configuration for `useNs()`. Applies to every splash shown through the
7
+ * returned API instance, keeping visual behavior consistent.
8
+ */
9
+ export interface NsOptions {
10
+ /**
11
+ * Milliseconds to keep the splash invisible after `show()`.
12
+ * If it is hidden before the delay elapses, it never becomes visible.
13
+ * Prevents a flash of spinner on fast operations.
14
+ */
15
+ showDelay?: int
16
+ /**
17
+ * Minimum milliseconds the splash stays visible once shown.
18
+ * A `hide()` call arriving earlier is deferred until the minimum has passed.
19
+ * Prevents a barely-visible blink.
20
+ */
21
+ minDuration?: int
22
+ }
23
+
24
+ /**
25
+ * # Job
26
+ * An async unit of work tracked by a Nanosplash. The splash is guaranteed
27
+ * to be visible before the job is invoked.
28
+ */
29
+ export type NsJob<T> = () => Promise<T>
30
+
31
+ /**
32
+ * # Label
33
+ * The text displayed beside the spinner, or falsy for spinner only.
34
+ */
35
+ export type NsLabel = string | null | undefined
36
+
37
+ /**
38
+ * # Labeled job
39
+ * A job with a label. While the job runs, the splash shows the label.
40
+ */
41
+ export type NsLabeledJob<T> = [NsLabel, NsJob<T>]
42
+
43
+ /**
44
+ * # Show input
45
+ * Everything `show()` accepts as its first argument.
46
+ */
47
+ export type NsShowInput =
48
+ | NsLabel
49
+ | NsLabeledJob<unknown>
50
+ | readonly NsLabeledJob<unknown>[]
51
+
52
+ /**
53
+ * # Job results
54
+ * Maps a tuple of labeled jobs to a tuple of each job's resolved return value.
55
+ */
56
+ export type NsJobResults<Jobs extends readonly NsLabeledJob<unknown>[]> = {
57
+ -readonly [K in keyof Jobs]: Jobs[K] extends NsLabeledJob<infer R>
58
+ ? Awaited<R>
59
+ : never
60
+ }
61
+
4
62
  /**
5
63
  * # Nanosplash
6
64
  * @author Isak Hauge <isakhauge@icloud.com>
@@ -8,61 +66,63 @@ import type { int } from '../semantic/number'
8
66
  export interface INanosplash {
9
67
  /**
10
68
  * # Show
11
- * Display Nanosplash inside the window.
12
- * @param {string | null | undefined} text Optional text. Pass falsy to display spinner only.
13
- * @param {string | Element | undefined} inside Optional element reference
14
- * @returns {number | null} The internal ID of the created Nanosplash.
69
+ * Display a splash, globally or inside a container.
70
+ * @param label Optional label. Pass falsy to display the spinner only.
71
+ * @param inside Optional container: an `Element` or a CSS selector.
72
+ * Omit to target the document body.
73
+ * @returns The splash's ID, or `null` if `inside` could not be resolved.
15
74
  * @example
16
- * // Show spinner only
17
- * ns.show()
18
- * @example
19
- * // Show spinner and text
20
- * ns.show('Loading ...')
75
+ * ns.show() // spinner only
76
+ * ns.show('Loading…') // spinner and label
77
+ * ns.show('Loading…', '#my-div') // inside a container (selector)
78
+ * ns.show(null, myElement) // inside a container (element), no label
79
+ */
80
+ show(label?: NsLabel, inside?: ElementRef | HTMLElement): int | null
81
+ /**
82
+ * # Show (single job)
83
+ * Display a splash for the lifetime of one job: show it with the label
84
+ * before the job starts and hide it when the job settles (resolve or
85
+ * reject). Pass the job's result through.
86
+ * @param job A `[label, job]` pair.
87
+ * @param inside Optional container: an `Element` or a CSS selector.
88
+ * @returns The job's resolved value.
21
89
  * @example
22
- * // Show spinner and text inside element
23
- * const div = document.createElement('div')
24
- * div.id = 'my-div'
25
- * document.body.append(div)
26
- *
27
- * // Alternative 1: Using node
28
- * ns.show('Loading ...', div)
29
- *
30
- * // Alternative 2: Using selector
31
- * ns.show('Loading ...', '#my-div')
90
+ * const user = await ns.show(['Loading user…', () => fetchUser()])
91
+ */
92
+ show<T>(
93
+ job: NsLabeledJob<T>,
94
+ inside?: ElementRef | HTMLElement,
95
+ ): Promise<Awaited<T>>
96
+ /**
97
+ * # Show (job sequence)
98
+ * Display one splash across sequential labeled jobs, updating the label as
99
+ * each job starts. Resolve with the jobs' results as a typed tuple, in
100
+ * input order. Fail fast: the first rejection hides the splash,
101
+ * propagates, and skips the remaining jobs.
102
+ * @param jobs An array of `[label, job]` pairs.
103
+ * @param inside Optional container: an `Element` or a CSS selector.
104
+ * @returns The jobs' resolved values, in input order.
32
105
  * @example
33
- * // Show spinner only, inside element
34
- * const div = document.createElement('div')
35
- * div.id = 'my-div'
36
- * document.body.append(div)
37
- *
38
- * // Alternative 1: Using node
39
- * ns.show(null, div)
40
- *
41
- * // Alternative 2: Using selector
42
- * ns.show(null, '#my-div')
106
+ * const [user, posts] = await ns.show([
107
+ * ['Loading user…', () => fetchUser()],
108
+ * ['Loading posts…', () => fetchPosts()],
109
+ * ])
43
110
  */
44
- show(
45
- text?: string | null | undefined,
46
- inside?: ElementRef | HTMLElement
47
- ): int | null
111
+ show<Jobs extends readonly NsLabeledJob<unknown>[]>(
112
+ jobs: readonly [...Jobs],
113
+ inside?: ElementRef | HTMLElement,
114
+ ): Promise<NsJobResults<Jobs>>
48
115
  /**
49
116
  * # Hide
50
- * Remove Nanosplash in multiple ways depending on the passed argument.
51
- * - Pass ID: Remove specific.
52
- * - Pass '*': Remove all in window.
53
- * - Pass nothing: Remove in FIFO order.
54
- * @param id The internal ID, an asterisk, or nothing.
55
- * @example
56
- * // Remove the first Nanosplash in the queue (FIFO)
57
- * ns.hide()
58
- * @example
59
- * // Remove specific Nanosplash in case you have multiple
60
- * const idA: number = ns.show(null, '#div-a') // 1700000000000
61
- * const idB: number = ns.show(null, '#div-b') // 1800000000000
62
- * ns.hide(idB) // Only hides Nanosplash inside the element with ID div-b
117
+ * Remove one or more splashes.
118
+ * - Pass an ID: remove that splash.
119
+ * - Pass `'*'`: remove every splash.
120
+ * - Pass nothing: remove the oldest splash (FIFO).
121
+ * @param id A splash ID, `'*'`, or nothing.
63
122
  * @example
64
- * // Hide all Nanosplash elements in the DOM
65
- * ns.hide('*')
123
+ * ns.hide() // oldest splash
124
+ * ns.hide(id) // specific splash, using the ID returned by show()
125
+ * ns.hide('*') // every splash
66
126
  */
67
127
  hide(id?: int | '*'): void
68
128
  /**