nanosplash 4.1.4 → 4.1.5
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/README.md +152 -8
- package/dist/cjs/ns.cjs.js +1 -1
- package/dist/es/ns.es.js +54 -28
- package/dist/iife/ns.iife.js +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,27 +1,171 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">Nanosplash</h1>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center"><strong>The tiny loading screen for web artisans</strong></p>
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/nanosplash"><img src="https://img.shields.io/npm/v/nanosplash?label=npm" alt="npm version"></a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/nanosplash"><img src="https://img.shields.io/npm/dw/nanosplash" alt="npm downloads per week"></a>
|
|
8
|
+
<a href="https://bundlejs.com/?q=nanosplash"><img src="https://img.shields.io/bundlejs/size/nanosplash?label=gzipped" alt="bundle size, minified and gzipped"></a>
|
|
9
|
+
<a href="https://github.com/isakhauge/nanosplash/actions/workflows/ci.yml"><img src="https://github.com/isakhauge/nanosplash/actions/workflows/ci.yml/badge.svg" alt="CI status"></a>
|
|
10
|
+
<a href="https://coveralls.io/github/isakhauge/nanosplash?branch=main"><img src="https://coveralls.io/repos/github/isakhauge/nanosplash/badge.svg?branch=main" alt="Coverage status"></a>
|
|
11
|
+
<a href="./types/global.d.ts"><img src="https://img.shields.io/badge/TypeScript-ready-3178c6?logo=typescript&logoColor=white" alt="TypeScript ready"></a>
|
|
12
|
+
<a href="./LICENSE"><img src="https://img.shields.io/github/license/isakhauge/nanosplash?color=green" alt="MIT license"></a>
|
|
13
|
+
</p>
|
|
7
14
|
|
|
8
|
-
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="https://raw.githubusercontent.com/isakhauge/nanosplash/main/assets/demo.svg" width="720" alt="Nanosplash demo: a translucent overlay with a spinner and the labels 'Loading user' and 'Loading posts' appears over a page, then fades away">
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="https://raw.githack.com/isakhauge/nanosplash/main/docs/index.html"><strong>Live demo</strong></a>
|
|
21
|
+
·
|
|
22
|
+
<a href="./docs.md"><strong>Full documentation</strong></a>
|
|
23
|
+
·
|
|
24
|
+
<a href="./CHANGELOG.md"><strong>Changelog</strong></a>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
## Why Nanosplash
|
|
28
|
+
|
|
29
|
+
- **~2.6 kB gzipped.** Smaller than most icons. Nothing else to ship.
|
|
30
|
+
- **Zero dependencies.** Plain TypeScript, plain CSS, injected once on first use.
|
|
31
|
+
- **Framework-agnostic.** Works in vanilla JS, React, Vue, Svelte, Angular, or any plain `<script>` tag.
|
|
32
|
+
- **Three-call API.** `show()`, `hide()`, `version`. Nothing to learn, nothing to configure.
|
|
33
|
+
- **Fullscreen or scoped.** Overlay the whole page or any element you pass in, by selector or reference.
|
|
34
|
+
- **Labeled async jobs.** Hand `show()` your promises. The label updates per step and the splash hides itself when done.
|
|
35
|
+
- **Anti-flicker timing.** Optional `showDelay` and `minDuration` so fast requests never blink a spinner.
|
|
36
|
+
- **Accessible and themeable.** `role="status"`, `aria-live`, reduced-motion support, automatic dark mode, and `--ns-*` CSS custom properties.
|
|
37
|
+
- **ESM, CJS, and IIFE builds** with bundled TypeScript types.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
9
40
|
|
|
10
41
|
```bash
|
|
11
42
|
npm install nanosplash
|
|
12
43
|
```
|
|
13
44
|
|
|
45
|
+
Or drop it in from a CDN with no build step. This exposes a global `useNs`:
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<script src="https://unpkg.com/nanosplash/dist/iife/ns.iife.js"></script>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
14
53
|
```js
|
|
54
|
+
import { useNs } from 'nanosplash'
|
|
55
|
+
|
|
15
56
|
const ns = useNs()
|
|
16
57
|
|
|
17
58
|
ns.show('Loading')
|
|
18
|
-
|
|
59
|
+
// ...do work...
|
|
19
60
|
ns.hide()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
### Fullscreen or scoped to an element
|
|
20
66
|
|
|
21
|
-
|
|
67
|
+
```js
|
|
68
|
+
ns.show() // fullscreen spinner, no text
|
|
69
|
+
ns.show('Loading') // fullscreen spinner with a label
|
|
70
|
+
ns.show('Fetching data', '#dashboard') // inside an element, by CSS selector
|
|
71
|
+
ns.show('Please wait', document.querySelector('.card')) // by element reference
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Calling `show()` again on the same container updates the label instead of stacking a second spinner. The fullscreen splash never touches the page's scroll position.
|
|
75
|
+
|
|
76
|
+
### Run work under a splash
|
|
77
|
+
|
|
78
|
+
Pass a `[label, job]` pair and the splash lives exactly as long as the job. Pass several pairs and they run in order under one splash, with the label updating as each step starts. Results come back as a typed tuple.
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
const user = await ns.show(['Loading user', () => fetchUser()])
|
|
82
|
+
|
|
83
|
+
const [profile, posts] = await ns.show([
|
|
22
84
|
['Loading user', () => fetchUser()],
|
|
23
85
|
['Loading posts', () => fetchPosts()],
|
|
24
86
|
])
|
|
25
87
|
```
|
|
26
88
|
|
|
27
|
-
|
|
89
|
+
Rejections propagate after the splash is hidden, so a normal `try`/`catch` works.
|
|
90
|
+
|
|
91
|
+
### Hide
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
ns.hide() // hide the oldest active splash
|
|
95
|
+
ns.hide(id) // hide one specific splash, using the id returned by show()
|
|
96
|
+
ns.hide('*') // hide every active splash
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Anti-flicker timing
|
|
100
|
+
|
|
101
|
+
Configure once and every splash from that instance follows the same rules. The splash stays invisible for `showDelay` ms and, once visible, stays for at least `minDuration` ms. Requests that finish inside the delay never show a spinner at all.
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
const ns = useNs({ showDelay: 150, minDuration: 400 })
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Theming
|
|
108
|
+
|
|
109
|
+
Nanosplash ships light and dark defaults that follow `prefers-color-scheme`, with a `data-theme="light|dark"` attribute on the root element as an override. All defaults are declared at zero specificity, so any rule of yours wins regardless of load order.
|
|
110
|
+
|
|
111
|
+
```css
|
|
112
|
+
:root {
|
|
113
|
+
--ns-color: tomato;
|
|
114
|
+
--ns-size: 24px;
|
|
115
|
+
--ns-bg: rgba(0, 0, 0, 0.8);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Theme only the loaders inside one panel */
|
|
119
|
+
#dashboard {
|
|
120
|
+
--ns-color: white;
|
|
121
|
+
--ns-bg: rgba(0, 0, 0, 0.6);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
| Property | Default | Controls |
|
|
126
|
+
| ------------------- | ------------------------------- | ------------------------------------- |
|
|
127
|
+
| `--ns-color` | `DarkSlateGray` | Spinner and text color |
|
|
128
|
+
| `--ns-size` | `20px` | Base size; the spinner scales from it |
|
|
129
|
+
| `--ns-font` | `'Inter', 'Helvetica', 'Arial'` | Label font stack |
|
|
130
|
+
| `--ns-weight` | `400` | Label font weight |
|
|
131
|
+
| `--ns-bg` | `rgba(255, 255, 255, 0.9)` | Overlay background |
|
|
132
|
+
| `--ns-blur` | `blur(5px)` | Backdrop blur |
|
|
133
|
+
| `--ns-shadow-color` | `rgba(0, 0, 0, 0.25)` | Text and spinner shadow |
|
|
134
|
+
| `--ns-z-index` | `9999999999` | Overlay z-index |
|
|
135
|
+
|
|
136
|
+
## Accessibility
|
|
137
|
+
|
|
138
|
+
- The splash has `role="status"` and `aria-live="polite"`, so the label is announced when it appears or changes.
|
|
139
|
+
- The host container gets `aria-busy="true"` while a splash is active and loses it on hide.
|
|
140
|
+
- Under `prefers-reduced-motion: reduce`, fades and entrance animations are disabled and the spinner rotates slowly with a static arc.
|
|
141
|
+
|
|
142
|
+
## API at a glance
|
|
143
|
+
|
|
144
|
+
| Call | Returns | Description |
|
|
145
|
+
| ---------------------------------- | ------------------------- | --------------------------------------------------------------------------- |
|
|
146
|
+
| `useNs(options?)` | `{ show, hide, version }` | Create an API instance. `options` sets `showDelay` and `minDuration`. |
|
|
147
|
+
| `show(label?, inside?)` | `number \| null` | Show a splash. Returns its id, or `null` if `inside` could not be resolved. |
|
|
148
|
+
| `show([label, job], inside?)` | `Promise<T>` | Run one job under a splash and resolve with its result. |
|
|
149
|
+
| `show([[label, job], …], inside?)` | `Promise<[T1, T2, …]>` | Run jobs sequentially under one splash and resolve with a tuple. |
|
|
150
|
+
| `hide(id?)` | `void` | Hide the oldest splash, a specific id, or all with `'*'`. |
|
|
151
|
+
| `version` | `string` | The installed Nanosplash version. |
|
|
152
|
+
|
|
153
|
+
`inside` accepts an `Element` or a CSS selector string. See the [full documentation](./docs.md) for every detail, including instance recycling, FIFO ordering, and host classes.
|
|
154
|
+
|
|
155
|
+
## Module formats
|
|
156
|
+
|
|
157
|
+
| Format | Entry | Use |
|
|
158
|
+
| ------ | ---------------------- | ----------------------------------------------------- |
|
|
159
|
+
| ESM | `dist/es/ns.es.js` | `import { useNs } from 'nanosplash'` |
|
|
160
|
+
| CJS | `dist/cjs/ns.cjs.js` | `const { useNs } = require('nanosplash')` |
|
|
161
|
+
| IIFE | `dist/iife/ns.iife.js` | `<script>` tag from unpkg or jsDelivr, global `useNs` |
|
|
162
|
+
|
|
163
|
+
Types ship in the package, so editors pick them up automatically.
|
|
164
|
+
|
|
165
|
+
## Contributing
|
|
166
|
+
|
|
167
|
+
Found a bug or want a feature? [Open an issue](https://github.com/isakhauge/nanosplash/issues) or send a pull request.
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
[MIT](./LICENSE) © Isak Hauge
|
package/dist/cjs/ns.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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)}}: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-shadow-color:#00000040;--ns-show-delay:0s}@media (prefers-color-scheme:dark){:where(:root:not([data-theme=light])){--ns-color:#fafafa;--ns-bg:#09090bd9;--ns-shadow-color:#ffffff59}}:where(:root[data-theme=dark]){--ns-color:#fafafa;--ns-bg:#09090bd9;--ns-shadow-color:#ffffff59}.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);--shadowColor:var(--ns-shadow-color);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);backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit;position:absolute;bottom:0;right:0}body.nsh:before,body.nsh>.ns{position:fixed;inset: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;
|
|
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)}}: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-shadow-color:#00000040;--ns-show-delay:0s}@media (prefers-color-scheme:dark){:where(:root:not([data-theme=light])){--ns-color:#fafafa;--ns-bg:#09090bd9;--ns-shadow-color:#ffffff59}}:where(:root[data-theme=dark]){--ns-color:#fafafa;--ns-bg:#09090bd9;--ns-shadow-color:#ffffff59}.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);--shadowColor:var(--ns-shadow-color);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);backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit;position:absolute;bottom:0;right:0}body.nsh:before,body.nsh>.ns{position:fixed;inset: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);max-width:calc(80% - var(--relSize));white-space:nowrap;text-overflow:ellipsis;text-shadow:0 0 .06rem var(--shadowColor);filter:drop-shadow(0 0 .07rem var(--shadowColor));animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);overflow:hidden}body.nsh>.ns .nst{max-width:min(40rem,80dvw)}.nss{width:var(--relSize);height:var(--relSize);animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);filter:drop-shadow(0 0 .07rem var(--shadowColor));display:block}.nss>svg{width:inherit;height:inherit;stroke-width:8px;transform-origin:50%;will-change:transform;backface-visibility:hidden;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{transform-origin:50%;will-change:transform;backface-visibility:hidden;animation:6s linear infinite nsRotate}.nss .path{stroke-dasharray:90 150;stroke-dashoffset:-35px;animation:none}}`,t=()=>globalThis.document,n=()=>t().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.5`,f=1,p=e=>Array.isArray(e)&&typeof e[1]==`function`,m=300,h=()=>globalThis.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches??!1,g=(e,t,n)=>typeof e.animate!=`function`||h()?Promise.resolve():e.animate(t,{duration:m,easing:n}).finished.catch(()=>void 0),_=m=>{let h=()=>i(t(),u.ns),_=()=>{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.nsSpinner,e),s(l.nsText));return t.setAttribute(`role`,`status`),t.setAttribute(`aria-live`,`polite`),t.nsId=f++,t},v=()=>h().filter(e=>e.nsHideTimer===void 0).sort((e,t)=>e.nsId-t.nsId),y=()=>v()[0]??null,b=(e,t)=>{let n=a(e,u.nsText);if(!t){n?.remove();return}if(!n){e.append(s(l.nsText,t));return}if(!n.textContent){n.textContent=t;return}if(n.textContent===t)return;let r=n.offsetWidth;n.textContent=t;let i=n.offsetWidth;g(n,[{opacity:0,transform:`translateY(10px)`},{opacity:1,transform:`none`}],`ease-out`),n.style.overflow=`visible`,g(n,[{width:`${r}px`},{width:`${i}px`}],`ease`).then(()=>{n.textContent===t&&(n.style.overflow=``)})},x=(e,t)=>{let n=t.firstElementChild;n?t.insertBefore(e,n):t.append(e),t.classList.add(l.nsHost),t.setAttribute(`aria-busy`,`true`)},S=(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`)},C=e=>r(e.children).find(e=>e.classList.contains(l.ns)),w=(e,t)=>{let r=t?o(t):n();if(!r)return null;A();let i,a=C(r);return a?(i=a,clearTimeout(i.nsHideTimer),i.nsHideTimer=void 0):(i=_(),x(i,r)),S(i,r),b(i,e??``),i.nsId},T=e=>{let t=e.parentElement;t&&(t.classList.remove(l.nsHost),t.removeAttribute(`aria-busy`),t.style.removeProperty(`--ns-show-delay`)),e.remove()},E=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(()=>T(e),i);return}T(e)},D=e=>h().find(t=>t.nsId===e)??null,O=e=>{e===`*`?h().forEach(E):E(typeof e==`number`?D(e):y())},k=()=>c(`<style id="ns">${e}</style>`),A=()=>{let e=a(t(),`#ns`),n=k();if(!e){t().head.append(n);return}e.textContent!==n.textContent&&e.replaceWith(n)},j=async(e,t)=>{if(e.length===0)return[];let n=[],r=null;try{for(let[i,a]of e)r=w(i,t),n.push(await a())}finally{r!==null&&O(r)}return n};return{show:((e,t)=>p(e)?j([e],t).then(e=>e[0]):Array.isArray(e)?j(e,t):w(e,t)),hide:O,version:d}};exports.useNs=_;
|
package/dist/es/ns.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/style/ns.css?inline
|
|
2
|
-
var e = () => globalThis.document, t = () =>
|
|
2
|
+
var e = () => globalThis.document, t = () => e().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
3
|
let r = e().createElement("div");
|
|
4
4
|
return t && r.classList.add(t), r.append(...n), r;
|
|
5
5
|
}, s = (e) => {
|
|
@@ -15,62 +15,88 @@ var e = () => globalThis.document, t = () => globalThis.document.body, n = (e) =
|
|
|
15
15
|
nsHost: "." + c.nsHost,
|
|
16
16
|
nsText: "." + c.nsText,
|
|
17
17
|
nsSpinner: "." + c.nsSpinner
|
|
18
|
-
}, u = "4.1.
|
|
19
|
-
|
|
18
|
+
}, u = "4.1.5", d = 1, f = (e) => Array.isArray(e) && typeof e[1] == "function", p = 300, m = () => globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? !1, h = (e, t, n) => typeof e.animate != "function" || m() ? Promise.resolve() : e.animate(t, {
|
|
19
|
+
duration: p,
|
|
20
|
+
easing: n
|
|
21
|
+
}).finished.catch(() => void 0), g = (p) => {
|
|
22
|
+
let m = () => r(e(), l.ns), g = () => {
|
|
20
23
|
let e = s("<svg viewBox=\"0 0 50 50\"><circle class=path cx=25 cy=25 r=20 fill=none /></svg>");
|
|
21
24
|
e.setAttribute("aria-hidden", "true");
|
|
22
|
-
let t = o(c.ns, o(c.
|
|
25
|
+
let t = o(c.ns, o(c.nsSpinner, e), o(c.nsText));
|
|
23
26
|
return t.setAttribute("role", "status"), t.setAttribute("aria-live", "polite"), t.nsId = d++, t;
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
}, _ = () => m().filter((e) => e.nsHideTimer === void 0).sort((e, t) => e.nsId - t.nsId), v = () => _()[0] ?? null, y = (e, t) => {
|
|
28
|
+
let n = i(e, l.nsText);
|
|
29
|
+
if (!t) {
|
|
30
|
+
n?.remove();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (!n) {
|
|
34
|
+
e.append(o(c.nsText, t));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (!n.textContent) {
|
|
38
|
+
n.textContent = t;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (n.textContent === t) return;
|
|
42
|
+
let r = n.offsetWidth;
|
|
43
|
+
n.textContent = t;
|
|
44
|
+
let a = n.offsetWidth;
|
|
45
|
+
h(n, [{
|
|
46
|
+
opacity: 0,
|
|
47
|
+
transform: "translateY(10px)"
|
|
48
|
+
}, {
|
|
49
|
+
opacity: 1,
|
|
50
|
+
transform: "none"
|
|
51
|
+
}], "ease-out"), n.style.overflow = "visible", h(n, [{ width: `${r}px` }, { width: `${a}px` }], "ease").then(() => {
|
|
52
|
+
n.textContent === t && (n.style.overflow = "");
|
|
53
|
+
});
|
|
54
|
+
}, b = (e, t) => {
|
|
29
55
|
let n = t.firstElementChild;
|
|
30
56
|
n ? t.insertBefore(e, n) : t.append(e), t.classList.add(c.nsHost), t.setAttribute("aria-busy", "true");
|
|
31
|
-
},
|
|
57
|
+
}, x = (e, t) => {
|
|
32
58
|
e.nsShownAt = performance.now(), e.nsShowDelay = p?.showDelay ?? 0, e.nsMinDuration = p?.minDuration ?? 0, t.style.setProperty("--ns-show-delay", e.nsShowDelay + "ms");
|
|
33
|
-
},
|
|
59
|
+
}, S = (e) => n(e.children).find((e) => e.classList.contains(c.ns)), C = (e, n) => {
|
|
34
60
|
let r = n ? a(n) : t();
|
|
35
61
|
if (!r) return null;
|
|
36
|
-
|
|
37
|
-
let i, o =
|
|
38
|
-
return o ? (i = o, clearTimeout(i.nsHideTimer), i.nsHideTimer = void 0) : (i =
|
|
39
|
-
},
|
|
62
|
+
k();
|
|
63
|
+
let i, o = S(r);
|
|
64
|
+
return o ? (i = o, clearTimeout(i.nsHideTimer), i.nsHideTimer = void 0) : (i = g(), b(i, r)), x(i, r), y(i, e ?? ""), i.nsId;
|
|
65
|
+
}, w = (e) => {
|
|
40
66
|
let t = e.parentElement;
|
|
41
67
|
t && (t.classList.remove(c.nsHost), t.removeAttribute("aria-busy"), t.style.removeProperty("--ns-show-delay")), e.remove();
|
|
42
|
-
},
|
|
68
|
+
}, T = (e) => {
|
|
43
69
|
if (!e || e.nsHideTimer !== void 0) return;
|
|
44
70
|
let t = e.nsShowDelay ?? 0, n = performance.now() - ((e.nsShownAt ?? 0) + t), r = t > 0 && n < 0, i = (e.nsMinDuration ?? 0) - n;
|
|
45
71
|
if (!r && i > 0) {
|
|
46
|
-
e.nsHideTimer = setTimeout(() =>
|
|
72
|
+
e.nsHideTimer = setTimeout(() => w(e), i);
|
|
47
73
|
return;
|
|
48
74
|
}
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
e === "*" ? m().forEach(
|
|
52
|
-
},
|
|
53
|
-
let t = i(e(), "#ns"), n =
|
|
75
|
+
w(e);
|
|
76
|
+
}, E = (e) => m().find((t) => t.nsId === e) ?? null, D = (e) => {
|
|
77
|
+
e === "*" ? m().forEach(T) : T(typeof e == "number" ? E(e) : v());
|
|
78
|
+
}, O = () => 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)}}: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-shadow-color:#00000040;--ns-show-delay:0s}@media (prefers-color-scheme:dark){:where(:root:not([data-theme=light])){--ns-color:#fafafa;--ns-bg:#09090bd9;--ns-shadow-color:#ffffff59}}:where(:root[data-theme=dark]){--ns-color:#fafafa;--ns-bg:#09090bd9;--ns-shadow-color:#ffffff59}.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);--shadowColor:var(--ns-shadow-color);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);backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit;position:absolute;bottom:0;right:0}body.nsh:before,body.nsh>.ns{position:fixed;inset: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);max-width:calc(80% - var(--relSize));white-space:nowrap;text-overflow:ellipsis;text-shadow:0 0 .06rem var(--shadowColor);filter:drop-shadow(0 0 .07rem var(--shadowColor));animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);overflow:hidden}body.nsh>.ns .nst{max-width:min(40rem,80dvw)}.nss{width:var(--relSize);height:var(--relSize);animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);filter:drop-shadow(0 0 .07rem var(--shadowColor));display:block}.nss>svg{width:inherit;height:inherit;stroke-width:8px;transform-origin:50%;will-change:transform;backface-visibility:hidden;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{transform-origin:50%;will-change:transform;backface-visibility:hidden;animation:6s linear infinite nsRotate}.nss .path{stroke-dasharray:90 150;stroke-dashoffset:-35px;animation:none}}</style>"), k = () => {
|
|
79
|
+
let t = i(e(), "#ns"), n = O();
|
|
54
80
|
if (!t) {
|
|
55
81
|
e().head.append(n);
|
|
56
82
|
return;
|
|
57
83
|
}
|
|
58
84
|
t.textContent !== n.textContent && t.replaceWith(n);
|
|
59
|
-
},
|
|
85
|
+
}, A = async (e, t) => {
|
|
60
86
|
if (e.length === 0) return [];
|
|
61
87
|
let n = [], r = null;
|
|
62
88
|
try {
|
|
63
|
-
for (let [i, a] of e) r =
|
|
89
|
+
for (let [i, a] of e) r = C(i, t), n.push(await a());
|
|
64
90
|
} finally {
|
|
65
|
-
r !== null &&
|
|
91
|
+
r !== null && D(r);
|
|
66
92
|
}
|
|
67
93
|
return n;
|
|
68
94
|
};
|
|
69
95
|
return {
|
|
70
|
-
show: ((e, t) => f(e) ?
|
|
71
|
-
hide:
|
|
96
|
+
show: ((e, t) => f(e) ? A([e], t).then((e) => e[0]) : Array.isArray(e) ? A(e, t) : C(e, t)),
|
|
97
|
+
hide: D,
|
|
72
98
|
version: u
|
|
73
99
|
};
|
|
74
100
|
};
|
|
75
101
|
//#endregion
|
|
76
|
-
export {
|
|
102
|
+
export { g as useNs };
|
package/dist/iife/ns.iife.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){var e=()=>globalThis.document,t=()=>
|
|
1
|
+
(function(){var e=()=>globalThis.document,t=()=>e().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.5`,d=1,f=e=>Array.isArray(e)&&typeof e[1]==`function`,p=300,m=()=>globalThis.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches??!1,h=(e,t,n)=>typeof e.animate!=`function`||m()?Promise.resolve():e.animate(t,{duration:p,easing:n}).finished.catch(()=>void 0),g=p=>{let m=()=>r(e(),l.ns),g=()=>{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.nsSpinner,e),o(c.nsText));return t.setAttribute(`role`,`status`),t.setAttribute(`aria-live`,`polite`),t.nsId=d++,t},_=()=>m().filter(e=>e.nsHideTimer===void 0).sort((e,t)=>e.nsId-t.nsId),v=()=>_()[0]??null,y=(e,t)=>{let n=i(e,l.nsText);if(!t){n?.remove();return}if(!n){e.append(o(c.nsText,t));return}if(!n.textContent){n.textContent=t;return}if(n.textContent===t)return;let r=n.offsetWidth;n.textContent=t;let a=n.offsetWidth;h(n,[{opacity:0,transform:`translateY(10px)`},{opacity:1,transform:`none`}],`ease-out`),n.style.overflow=`visible`,h(n,[{width:`${r}px`},{width:`${a}px`}],`ease`).then(()=>{n.textContent===t&&(n.style.overflow=``)})},b=(e,t)=>{let n=t.firstElementChild;n?t.insertBefore(e,n):t.append(e),t.classList.add(c.nsHost),t.setAttribute(`aria-busy`,`true`)},x=(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`)},S=e=>n(e.children).find(e=>e.classList.contains(c.ns)),C=(e,n)=>{let r=n?a(n):t();if(!r)return null;k();let i,o=S(r);return o?(i=o,clearTimeout(i.nsHideTimer),i.nsHideTimer=void 0):(i=g(),b(i,r)),x(i,r),y(i,e??``),i.nsId},w=e=>{let t=e.parentElement;t&&(t.classList.remove(c.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=>m().find(t=>t.nsId===e)??null,D=e=>{e===`*`?m().forEach(T):T(typeof e==`number`?E(e):v())},O=()=>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)}}: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-shadow-color:#00000040;--ns-show-delay:0s}@media (prefers-color-scheme:dark){:where(:root:not([data-theme=light])){--ns-color:#fafafa;--ns-bg:#09090bd9;--ns-shadow-color:#ffffff59}}:where(:root[data-theme=dark]){--ns-color:#fafafa;--ns-bg:#09090bd9;--ns-shadow-color:#ffffff59}.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);--shadowColor:var(--ns-shadow-color);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);backdrop-filter:var(--blur);z-index:calc(var(--zIdx) + 1);border-radius:inherit;position:absolute;bottom:0;right:0}body.nsh:before,body.nsh>.ns{position:fixed;inset: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);max-width:calc(80% - var(--relSize));white-space:nowrap;text-overflow:ellipsis;text-shadow:0 0 .06rem var(--shadowColor);filter:drop-shadow(0 0 .07rem var(--shadowColor));animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);overflow:hidden}body.nsh>.ns .nst{max-width:min(40rem,80dvw)}.nss{width:var(--relSize);height:var(--relSize);animation:.5s backwards nsAscend;animation-delay:var(--ns-show-delay);filter:drop-shadow(0 0 .07rem var(--shadowColor));display:block}.nss>svg{width:inherit;height:inherit;stroke-width:8px;transform-origin:50%;will-change:transform;backface-visibility:hidden;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{transform-origin:50%;will-change:transform;backface-visibility:hidden;animation:6s linear infinite nsRotate}.nss .path{stroke-dasharray:90 150;stroke-dashoffset:-35px;animation:none}}</style>`),k=()=>{let t=i(e(),`#ns`),n=O();if(!t){e().head.append(n);return}t.textContent!==n.textContent&&t.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)=>f(e)?A([e],t).then(e=>e[0]):Array.isArray(e)?A(e,t):C(e,t)),hide:D,version:u}};window.addEventListener(`load`,()=>{window.ns=g()})})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nanosplash",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The tiny loading screen for web artisans",
|
|
6
6
|
"keywords": [
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"vite"
|
|
22
22
|
],
|
|
23
23
|
"homepage": "https://github.com/isakhauge/nanosplash",
|
|
24
|
+
"license": "MIT",
|
|
24
25
|
"author": {
|
|
25
26
|
"name": "Isak K. Hauge",
|
|
26
27
|
"email": "isakhauge@icloud.com",
|