nanosplash 4.0.3 → 4.0.4

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.
Files changed (68) hide show
  1. package/.github/workflows/ci.prod.yml +43 -34
  2. package/.prettierrc +9 -10
  3. package/README.md +118 -52
  4. package/bun.lock +635 -0
  5. package/coverage/lcov-report/base.css +224 -0
  6. package/coverage/lcov-report/block-navigation.js +87 -0
  7. package/coverage/lcov-report/favicon.png +0 -0
  8. package/coverage/lcov-report/index.html +176 -0
  9. package/coverage/lcov-report/prettify.css +1 -0
  10. package/coverage/lcov-report/prettify.js +2 -0
  11. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  12. package/coverage/lcov-report/sorter.js +196 -0
  13. package/coverage/lcov-report/src/composables/index.html +116 -0
  14. package/coverage/lcov-report/src/composables/ns.ts.html +547 -0
  15. package/coverage/lcov-report/src/constants/index.html +116 -0
  16. package/coverage/lcov-report/src/constants/ns.ts.html +124 -0
  17. package/coverage/lcov-report/src/iife.ts.html +100 -0
  18. package/coverage/lcov-report/src/index.html +116 -0
  19. package/coverage/lcov-report/src/interfaces/INSElement.ts.html +100 -0
  20. package/coverage/lcov-report/src/interfaces/INanosplash.ts.html +304 -0
  21. package/coverage/lcov-report/src/interfaces/index.html +131 -0
  22. package/coverage/lcov-report/src/utils/dom-utils.ts.html +289 -0
  23. package/coverage/lcov-report/src/utils/index.html +116 -0
  24. package/coverage/lcov.info +271 -0
  25. package/eslint.config.js +7 -2
  26. package/package.json +74 -90
  27. package/src/composables/ns.ts +154 -0
  28. package/src/constants/ns.ts +13 -0
  29. package/src/iife.ts +5 -0
  30. package/src/interfaces/INSElement.ts +5 -0
  31. package/src/interfaces/INanosplash.ts +73 -0
  32. package/src/style/ns.css +134 -0
  33. package/src/types/dom.d.ts +3 -0
  34. package/src/types/generic/function.d.ts +1 -0
  35. package/src/types/global.d.ts +26 -0
  36. package/src/types/semantic/number.d.ts +1 -0
  37. package/src/types/semantic/string.d.ts +2 -0
  38. package/src/utils/dom-utils.ts +68 -0
  39. package/tests/ns.spec.ts +124 -0
  40. package/tsconfig.json +29 -21
  41. package/vite.config.ts +16 -25
  42. package/vitest.config.ts +20 -0
  43. package/.env +0 -1
  44. package/.github/workflows/ci.test.yml +0 -48
  45. package/.github/workflows/vitepress.prod.yml +0 -69
  46. package/LICENSE +0 -21
  47. package/docs/.vitepress/config.ts +0 -78
  48. package/docs/.vitepress/theme/css/style.css +0 -258
  49. package/docs/.vitepress/theme/index.ts +0 -16
  50. package/docs/.vitepress/theme/vue/Card.vue +0 -31
  51. package/docs/.vitepress/theme/vue/CardGrid.vue +0 -17
  52. package/docs/.vitepress/theme/vue/PlayGround.vue +0 -36
  53. package/docs/api/doc/hide.md +0 -48
  54. package/docs/api/doc/show.md +0 -46
  55. package/docs/api/start/customize.md +0 -30
  56. package/docs/api/start/features.md +0 -17
  57. package/docs/api/start/install.md +0 -61
  58. package/docs/api/start/playground.md +0 -16
  59. package/docs/api/start/usage.md +0 -22
  60. package/docs/index.md +0 -33
  61. package/docs/public/macos.jpg +0 -0
  62. package/src/style/ns.sass +0 -120
  63. package/src/ts/Nanosplash.ts +0 -176
  64. package/src/ts/main.ts +0 -5
  65. package/src/ts/types/NanosplashInterface.ts +0 -29
  66. package/src/ts/types/Types.ts +0 -16
  67. package/src/ts/types/global.d.ts +0 -14
  68. package/tests/Nanosplash.spec.ts +0 -207
package/src/style/ns.sass DELETED
@@ -1,120 +0,0 @@
1
- // Spinner animations:
2
- @keyframes nsRotate
3
- 100%
4
- transform: rotate(360deg)
5
-
6
- @keyframes nsDash
7
- 0%
8
- stroke-dasharray: 1, 150
9
- stroke-dashoffset: 0
10
- 50%
11
- stroke-dasharray: 90, 150
12
- stroke-dashoffset: -35
13
- 100%
14
- stroke-dasharray: 90, 150
15
- stroke-dashoffset: -124
16
-
17
- @keyframes nsFade
18
- from
19
- opacity: 0
20
- to
21
- opacity: 1
22
-
23
- @keyframes nsAscend
24
- from
25
- opacity: 0
26
- transform: translateY(13px)
27
- to
28
- opacity: 1
29
- transform: translateY(0)
30
-
31
- :root
32
- --ns-top: 0px
33
-
34
- %maximize
35
- width: 100%
36
- height: 100%
37
-
38
- %bottom-left
39
- bottom: 0
40
- right: 0
41
-
42
- %flex-center-xy
43
- display: flex
44
- justify-content: center
45
- align-items: center
46
-
47
- // Nanosplash host scoped varibles
48
- .nsh
49
- --color: DarkSlateGray
50
- --size: 20px
51
- --relSize: calc(var(--size) * 0.9)
52
- --font: 'Helvetica', 'Arial', sans-serif
53
- --weight: 400
54
- --bg: #{ rgba(White, 0.9) }
55
- --zIdx: 999999999
56
- --blur: blur(5px)
57
-
58
- // Nanosplash host
59
- .nsh
60
- position: relative
61
- z-index: var(--zIdx)
62
- &::before // The backdrop
63
- @extend %maximize
64
- @extend %bottom-left
65
- position: absolute
66
- background-color: var(--bg)
67
- content: ''
68
- backdrop-filter: var(--blur)
69
- -webkit-backdrop-filter: var(--blur)
70
- z-index: calc(var(--zIdx) + 1)
71
- border-radius: inherit
72
-
73
- // When Nanosplash host is the document body
74
- body.nsh
75
- &, &::before
76
- @extend %maximize
77
- position: fixed
78
- top: var(--ns-top)
79
- left: 0
80
-
81
- // Nanosplash (main element)
82
- .ns
83
- @extend %maximize
84
- @extend %bottom-left
85
- @extend %flex-center-xy
86
- position: absolute
87
- z-index: calc(var(--zIdx) + 2)
88
- animation: nsFade 2s
89
- gap: var(--relSize)
90
-
91
- // Nanosplash text
92
- .nst
93
- color: var(--color)
94
- font-size: var(--size)
95
- font-family: var(--font)
96
- font-weight: var(--weight)
97
- max-width: 80dvw
98
- white-space: nowrap
99
- text-overflow: ellipsis
100
- overflow: hidden
101
- text-shadow: 0 0 0.06rem #{ rgba(DarkSlateGray, .25) }
102
- filter: drop-shadow(0 0 0.07rem #{ rgba(Black, 0.25) })
103
- animation: nsAscend 0.5s
104
-
105
- // Nanosplash spinner
106
- .nss
107
- display: block
108
- width: var(--relSize)
109
- height: var(--relSize)
110
- animation: nsAscend 0.5s
111
- & > svg
112
- animation: nsRotate 2s linear infinite
113
- position: relative
114
- width: inherit
115
- height: inherit
116
- stroke-width: 8
117
- & .path
118
- stroke: var(--color)
119
- stroke-linecap: round
120
- animation: nsDash 1.5s ease-in-out infinite
@@ -1,176 +0,0 @@
1
- // @ts-strict
2
- import style from '../style/ns.sass?inline'
3
- import { NanosplashInterface } from './types/NanosplashInterface'
4
- import { ElementReference, NSElement } from './types/Types'
5
- import { version } from '../../package.json'
6
-
7
- /**
8
- * # Use Nanosplash
9
- * @returns Nanosplash API.
10
- */
11
- export const useNs = (): NanosplashInterface => {
12
- const doc = window.document
13
- const bod = doc.body
14
-
15
- const toArray = <T>(iter: Iterable<T> | ArrayLike<T>) => Array.from(iter)
16
- const all = (node: ParentNode, ref: string) =>
17
- toArray(node.querySelectorAll(ref))
18
- const first = (node: ParentNode, ref: string) => all(node, ref)[0] ?? null
19
-
20
- /**
21
- * # Get All Nanosplashes
22
- * @returns An array of all Nanosplash elements in the DOM.
23
- */
24
- const getAllNs = (): NSElement[] => all(doc, '.ns') as NSElement[]
25
-
26
- /**
27
- * # Parse Ref
28
- * @param ref Element reference which could be a selector or an Element.
29
- * @returns The Element node or null.
30
- */
31
- const parseRef = (ref: ElementReference): Element | null =>
32
- ref instanceof Element ? ref : first(doc, ref)
33
-
34
- /**
35
- * # Create HTMLDivElement
36
- * @param className Optional string containing class names.
37
- * @param children Optional list of Nodes or strings.
38
- * @returns The newly created HTMLDivElement.
39
- */
40
- const div = (
41
- className?: string,
42
- ...children: (Node | string)[]
43
- ): HTMLDivElement => {
44
- const node: HTMLDivElement = doc.createElement('div')
45
- if (className) node.classList.add(className)
46
- node.append(...children)
47
- return node
48
- }
49
-
50
- /**
51
- * # Parse
52
- * @param html HTML string.
53
- * @returns Parsed node.
54
- */
55
- const parse = (html: string): Node => {
56
- const node: HTMLDivElement = div()
57
- node.innerHTML = html
58
- return node.firstChild as Node
59
- }
60
-
61
- /**
62
- * # Make Nanosplash Element
63
- * @returns Nanpslash DOM element.
64
- */
65
- const makeNs = (): NSElement => {
66
- const circle: string = '<circle class=path cx=25 cy=25 r=20 fill=none />'
67
- const svg = parse(`<svg viewBox="0 0 50 50">${circle}</svg>`) as Element
68
- const node = div('ns', div('nst'), div('nss', svg)) as NSElement
69
- node.nsId = Date.now()
70
- return node
71
- }
72
-
73
- /**
74
- * # Peek Nanosplash Queue
75
- * Collect all Nanosplash elements in the DOM, sort them by their NS ID
76
- * in ascending order, and return the first element.
77
- * @returns The front of the queue.
78
- */
79
- const peekNsQueue = (): NSElement | null =>
80
- getAllNs().sort((a: NSElement, b: NSElement) => a.nsId - b.nsId)[0] ?? null
81
-
82
- /**
83
- * # Set Nanosplash Text
84
- * Replace the text node to trigger the animation which in turn is invoked
85
- * by the connected callback in the DOM.
86
- * @param ns Nanosplash element.
87
- * @param text Text to display adjacent to spinner.
88
- */
89
- const setNsText = (ns: NSElement, text: string): void => {
90
- const oldNsText = first(ns, '.nst') as Element | null
91
- if (!text) return oldNsText?.remove()
92
- const newNsText = div('nst', text)
93
- if (oldNsText) {
94
- oldNsText.replaceWith(newNsText)
95
- } else {
96
- ns.insertBefore(newNsText, ns.firstChild)
97
- }
98
- }
99
-
100
- /**
101
- * # Set Nanosplash Parent
102
- * Define where inside the DOM to spawn the Nansoplash.
103
- * @param ns Nanosplash element.
104
- * @param parent NS element's parent to be.
105
- */
106
- const setNsParent = (ns: NSElement, parent: Element): void => {
107
- const child = parent.firstElementChild
108
- if (child) {
109
- parent.insertBefore(ns, child)
110
- }
111
- parent.append(ns)
112
- parent.classList.add('nsh')
113
- }
114
-
115
- /**
116
- * # Show Nanosplash
117
- * @param text Text to display adjacent to spinner.
118
- * @param inside Selector or element in which the NS element will be spawned.
119
- * @returns The ID if the NS element — which is a UTC integer.
120
- */
121
- const show = (text?: string, inside?: string | Element): number | null => {
122
- const parent: Element = (inside ? parseRef(inside) ?? bod : bod) as Element
123
- let ns: NSElement
124
- const recycled: Element | undefined = toArray(parent.children)
125
- .filter(node => node.classList.contains('ns'))
126
- .at(0)
127
- const isNs = recycled?.classList.contains('ns')
128
- if (isNs) {
129
- ns = recycled as NSElement
130
- } else {
131
- ns = makeNs()
132
- setNsParent(ns, parent as Element)
133
- }
134
- setNsText(ns, text ?? '')
135
- const top: string = scrollY + 'px'
136
- bod.style.setProperty('--ns-top', top)
137
- return ns.nsId
138
- }
139
-
140
- /**
141
- * # Remove Nanosplash
142
- * @param ns Nanosplash elememnt.
143
- */
144
- const removeNs = (ns?: Element | null): void => {
145
- ns?.parentElement?.classList.remove('nsh')
146
- ns?.remove()
147
- }
148
-
149
- /**
150
- * # Find Nanosplash
151
- * @param id The UTC integer returned from the `show` function.
152
- * @returns Null or the corresponding Nanosplash element.
153
- */
154
- const findNs = (id: number) => getAllNs().find(v => v.nsId === id) ?? null
155
-
156
- /**
157
- * # Hide Nanosplash
158
- * Hide agnostically in FIFO order, specifically by ID, or all by the asterisk
159
- * which will delete all Nanosplashes.
160
- * @param id Optional ID of a Nanosplash element or '*' .
161
- */
162
- const hide = (id?: number | '*'): void => {
163
- if (id === '*') all(doc, '.ns').forEach(removeNs)
164
- else removeNs(typeof id === 'number' ? findNs(id) : peekNsQueue())
165
- }
166
-
167
- // Add CSS to window
168
- first(doc, '#ns')?.remove()
169
- bod.append(parse(`<style id="ns">${style}</style>`))
170
-
171
- return {
172
- show,
173
- hide,
174
- version,
175
- }
176
- }
package/src/ts/main.ts DELETED
@@ -1,5 +0,0 @@
1
- // @ts-strict
2
- import { useNs } from './Nanosplash'
3
- window.addEventListener('load', function () {
4
- this.ns = useNs()
5
- })
@@ -1,29 +0,0 @@
1
- import { DOMSelector, UTCInteger } from './Types'
2
-
3
- export interface NanosplashInterface {
4
- /**
5
- * # Show
6
- * Display Nanosplash inside the window.
7
- * @param {string | null | undefined} text Optional text. Pass falsy to display spinner only.
8
- * @param {string | Element | undefined} inside Optional element reference
9
- * @returns {number | null} The internal ID of the created Nanosplash.
10
- */
11
- show(
12
- text?: string | null | undefined,
13
- inside?: DOMSelector | HTMLElement
14
- ): UTCInteger | null
15
- /**
16
- * # Hide
17
- * Remove Nanosplash in multiple ways depending on the passed argument.
18
- * - Pass ID: Remove specific.
19
- * - Pass '*': Remove all in window.
20
- * - Pass nothing: Remove in FIFO order.
21
- * @param id The internal ID, an asterisk, or nothing.
22
- */
23
- hide(id?: UTCInteger | '*'): void
24
- /**
25
- * # Version
26
- * The current version of Nanosplash.
27
- */
28
- version: string
29
- }
@@ -1,16 +0,0 @@
1
- import { NanosplashInterface } from './NanosplashInterface'
2
-
3
- export type Integer = number
4
- export type UTCInteger = Integer
5
- export type DOMSelector = string
6
- export type ElementReference = DOMSelector | Element
7
-
8
- export interface NSElement extends HTMLDivElement {
9
- nsId: number
10
- }
11
-
12
- declare global {
13
- interface Window {
14
- ns: NanosplashInterface
15
- }
16
- }
@@ -1,14 +0,0 @@
1
- import { NanosplashInterface } from './NanosplashInterface'
2
-
3
- interface IWindow {
4
- ns: NanosplashInterface
5
- }
6
-
7
- declare global {
8
- interface Window extends IWindow {}
9
- interface window extends IWindow {}
10
- interface globalThis extends globalThis {
11
- Window: IWindow
12
- window: IWindow
13
- }
14
- }
@@ -1,207 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { useNs } from '../src/ts/Nanosplash'
3
- import { NSElement } from '../src/ts/types/Types'
4
-
5
- const ns = (window.ns = useNs())
6
- const get = (selector: string) => document.querySelector(selector)
7
- const getAll = (selector: string) =>
8
- Array.from(document.querySelectorAll(selector))
9
- const allNs = () => getAll('.ns') as NSElement[]
10
- const nsCount = () => allNs().length
11
-
12
- const div = (id: string) => {
13
- const node = document.createElement('div')
14
- node.id = id
15
- return node
16
- }
17
-
18
- describe('Global access', () => {
19
- it('should have "ns"', () => {
20
- expect(window.ns).toBeDefined()
21
- expect(window.ns).toBe(ns)
22
- })
23
-
24
- it('should have the "ns.show" function', () => {
25
- expect(window.ns.show).toBeDefined()
26
- expect(window.ns.show).toBeTypeOf('function')
27
- })
28
-
29
- it('should have the "ns.hide" function', () => {
30
- expect(window.ns.hide).toBeDefined()
31
- expect(window.ns.hide).toBeTypeOf('function')
32
- })
33
- })
34
-
35
- describe('useNs hook', () => {
36
- it('should return the Nanosplash API', () => {
37
- const instance = useNs()
38
- expect(instance.show).toBeTypeOf('function')
39
- expect(instance.hide).toBeTypeOf('function')
40
- })
41
- })
42
-
43
- describe('Nanosplash API', () => {
44
- describe('show / hide cleanup', () => {
45
- describe('show', () => {
46
- it('should make the body host', () => {
47
- const id = ns.show()
48
- const bod = document.body
49
- const bodyIsHost = bod.classList.contains('nsh')
50
- expect(bodyIsHost).toBe(true)
51
- ns.hide(id as number)
52
- expect(nsCount()).toBe(0)
53
- })
54
-
55
- it('should create a Nanosplash element', () => {
56
- const id = ns.show()
57
- const nsElement = get('body > .ns') as NSElement
58
- expect(nsElement).toBeInstanceOf(HTMLDivElement)
59
- ns.hide(id as number)
60
- expect(nsCount()).toBe(0)
61
- })
62
-
63
- it('should have the correct internal ID', () => {
64
- const id = ns.show()
65
- const nsElement = get('body > .ns') as NSElement
66
- expect(id).toBe(nsElement.nsId)
67
- ns.hide(id as number)
68
- expect(nsCount()).toBe(0)
69
- })
70
- })
71
-
72
- describe('hide', () => {
73
- it('should be able to hide the element', () => {
74
- const id = ns.show()
75
- expect(nsCount()).toBeGreaterThan(0)
76
- ns.hide(id as number)
77
- expect(nsCount()).toBe(0)
78
- })
79
-
80
- it('should undo body as host', () => {
81
- const id = ns.show()
82
- ns.hide(id as number)
83
- const bodyIsNoLongerHost = document.body.classList.contains('nsh')
84
- expect(bodyIsNoLongerHost).toBe(false)
85
- expect(nsCount()).toBe(0)
86
- })
87
-
88
- it('should remove the Nanosplash element from DOM', () => {
89
- const id = ns.show()
90
- const nsElement = get('body > .ns') as NSElement
91
- ns.hide(id as number)
92
- expect(nsElement?.isConnected).toBe(false)
93
- expect(nsCount()).toBe(0)
94
- })
95
- })
96
- })
97
-
98
- describe('show()', () => {
99
- it('should inject a Nanosplash in the body element', () => {
100
- const id = ns.show()
101
- const elements = allNs() as NSElement[]
102
- const element = elements.find((v: NSElement) => v.nsId === id)
103
- expect(element).toBeInstanceOf(HTMLDivElement)
104
- ns.hide(id as number)
105
- expect(nsCount()).toBe(0)
106
- })
107
-
108
- it('should only display a spinner', () => {
109
- const id = ns.show()
110
- const nst = get('.ns > .nst')
111
- const nss = get('.ns > .nss')
112
- expect(nst).toBeNull()
113
- expect(nss).toBeInstanceOf(HTMLDivElement)
114
- ns.hide(id as number)
115
- expect(nsCount()).toBe(0)
116
- })
117
-
118
- it('should recycle elements', () => {
119
- const idFirst = ns.show()
120
- const idSecond = ns.show()
121
- expect(nsCount()).toBe(1)
122
- expect(idFirst).toBe(idSecond)
123
- ns.hide()
124
- ns.hide()
125
- expect(nsCount()).toBe(0)
126
- })
127
- })
128
-
129
- describe('show("foo", "#bar")', () => {
130
- it('should display text inside #bar', () => {
131
- const bar = div('bar')
132
- document.body.append(bar)
133
- const id = ns.show('foo', '#bar')
134
- expect(nsCount()).toBe(1)
135
- const selector = '#bar.nsh > .ns > .nst'
136
- const nst = get(selector)
137
- expect(nst).toBeInstanceOf(HTMLDivElement)
138
- expect((nst as HTMLDivElement).innerHTML).toBe('foo')
139
- ns.hide(id as number)
140
- bar.remove()
141
- expect(nsCount()).toBe(0)
142
- })
143
- })
144
-
145
- describe('hide()', () => {
146
- it('should hide in FIFO order', () => {
147
- document.body.append(div('a'), div('b'), div('c'), div('d'))
148
-
149
- ns.show(null, '#a')
150
- ns.show(null, '#b')
151
- ns.show(null, '#c')
152
- ns.show(null, '#d')
153
-
154
- const theseExist = (...ids: string[]) => ids.every(id => get(id))
155
-
156
- expect(theseExist('#a', '#b', '#c', '#d')).toBe(true)
157
- ns.hide()
158
- expect(theseExist('#b', '#c', '#d')).toBe(true)
159
- ns.hide()
160
- expect(theseExist('#c', '#d')).toBe(true)
161
- ns.hide()
162
- expect(theseExist('#d')).toBe(true)
163
- ns.hide()
164
-
165
- expect(nsCount()).toBe(0)
166
- getAll('#a, #b, #c, #d').forEach(node => node.remove())
167
- expect(nsCount()).toBe(0)
168
- })
169
- })
170
-
171
- describe('hide(id)', () => {
172
- it('should delete a specific Nanosplash', () => {
173
- const a = div('a')
174
- const b = div('b')
175
- document.body.append(a, b)
176
- const idA = ns.show(null, '#a')
177
- const idB = ns.show(null, '#b')
178
- const bothExist = allNs().every(node => [idA, idB].includes(node.nsId))
179
- expect(bothExist).toBe(true)
180
- expect(nsCount()).toBe(2)
181
- ns.hide(idB as number)
182
- expect(nsCount()).toBe(1)
183
- expect(allNs()[0].nsId).toBe(idA)
184
- ns.hide(idA as number)
185
- a.remove()
186
- b.remove()
187
- expect(nsCount()).toBe(0)
188
- })
189
- })
190
-
191
- describe('hide(*)', () => {
192
- it('should hide all Nanosplashes', () => {
193
- document.body.append(div('a'), div('b'), div('c'))
194
-
195
- ns.show(null, '#a')
196
- ns.show(null, '#b')
197
- ns.show(null, '#c')
198
-
199
- expect(nsCount()).toBe(3)
200
- ns.hide('*')
201
- expect(nsCount()).toBe(0)
202
-
203
- getAll('#a, #b, #c').forEach(node => node.remove())
204
- expect(nsCount()).toBe(0)
205
- })
206
- })
207
- })