nanosplash 4.0.2 → 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.
- package/.github/workflows/ci.prod.yml +68 -0
- package/.prettierrc +9 -10
- package/README.md +118 -51
- package/bun.lock +635 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +176 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/composables/index.html +116 -0
- package/coverage/lcov-report/src/composables/ns.ts.html +547 -0
- package/coverage/lcov-report/src/constants/index.html +116 -0
- package/coverage/lcov-report/src/constants/ns.ts.html +124 -0
- package/coverage/lcov-report/src/iife.ts.html +100 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/interfaces/INSElement.ts.html +100 -0
- package/coverage/lcov-report/src/interfaces/INanosplash.ts.html +304 -0
- package/coverage/lcov-report/src/interfaces/index.html +131 -0
- package/coverage/lcov-report/src/utils/dom-utils.ts.html +289 -0
- package/coverage/lcov-report/src/utils/index.html +116 -0
- package/coverage/lcov.info +271 -0
- package/eslint.config.js +7 -2
- package/package.json +74 -88
- package/src/composables/ns.ts +154 -0
- package/src/constants/ns.ts +13 -0
- package/src/iife.ts +5 -0
- package/src/interfaces/INSElement.ts +5 -0
- package/src/interfaces/INanosplash.ts +73 -0
- package/src/style/ns.css +134 -0
- package/src/types/dom.d.ts +3 -0
- package/src/types/generic/function.d.ts +1 -0
- package/src/types/global.d.ts +26 -0
- package/src/types/semantic/number.d.ts +1 -0
- package/src/types/semantic/string.d.ts +2 -0
- package/src/utils/dom-utils.ts +68 -0
- package/tests/ns.spec.ts +124 -0
- package/tsconfig.json +29 -21
- package/vite.config.ts +18 -10
- package/vitest.config.ts +20 -0
- package/.github/workflows/ci.yml +0 -62
- package/.github/workflows/vitepress.yml +0 -68
- package/LICENSE +0 -21
- package/docs/.vitepress/config.ts +0 -78
- package/docs/.vitepress/theme/css/style.css +0 -258
- package/docs/.vitepress/theme/index.ts +0 -16
- package/docs/.vitepress/theme/vue/Card.vue +0 -31
- package/docs/.vitepress/theme/vue/CardGrid.vue +0 -17
- package/docs/.vitepress/theme/vue/PlayGround.vue +0 -36
- package/docs/api/doc/hide.md +0 -48
- package/docs/api/doc/show.md +0 -46
- package/docs/api/start/customize.md +0 -30
- package/docs/api/start/features.md +0 -17
- package/docs/api/start/install.md +0 -61
- package/docs/api/start/playground.md +0 -16
- package/docs/api/start/usage.md +0 -22
- package/docs/index.md +0 -33
- package/docs/public/macos.jpg +0 -0
- package/src/style/ns.sass +0 -120
- package/src/ts/Nanosplash.ts +0 -177
- package/src/ts/main.ts +0 -5
- package/src/ts/types/NanosplashInterface.ts +0 -26
- package/src/ts/types/Types.ts +0 -16
- package/src/ts/types/global.d.ts +0 -14
- package/tests/Nanosplash.spec.ts +0 -25
package/package.json
CHANGED
|
@@ -1,90 +1,76 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"build:docs": "pnpm docs:build",
|
|
77
|
-
"preview": "vite preview",
|
|
78
|
-
"test": "vitest",
|
|
79
|
-
"test:ui": "vitest --ui",
|
|
80
|
-
"lint": "eslint src/ts/**/*.ts --fix",
|
|
81
|
-
"coverage": "vitest --coverage",
|
|
82
|
-
"docs:dev": "pnpm exec vitepress dev docs",
|
|
83
|
-
"docs:build": "pnpm exec vitepress build docs",
|
|
84
|
-
"docs:preview": "pnpm exec vitepress preview docs"
|
|
85
|
-
},
|
|
86
|
-
"source": "./src/main.ts",
|
|
87
|
-
"type": "module",
|
|
88
|
-
"types": "./src/ts/types/global.d.ts",
|
|
89
|
-
"unpkg": "./dist/iife/ns.iife.js"
|
|
2
|
+
"name": "nanosplash",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "4.0.4",
|
|
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
|
+
"description": "The tiny loading screen for web artisans",
|
|
16
|
+
"homepage": "https://github.com/isakhauge/nanosplash",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"loading",
|
|
19
|
+
"loader",
|
|
20
|
+
"screen",
|
|
21
|
+
"splash",
|
|
22
|
+
"nano",
|
|
23
|
+
"nanosplash",
|
|
24
|
+
"small",
|
|
25
|
+
"tiny",
|
|
26
|
+
"es",
|
|
27
|
+
"esm",
|
|
28
|
+
"ts",
|
|
29
|
+
"typescript",
|
|
30
|
+
"iife",
|
|
31
|
+
"cdn",
|
|
32
|
+
"vite"
|
|
33
|
+
],
|
|
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\"",
|
|
43
|
+
"test": "bun run vitest"
|
|
44
|
+
},
|
|
45
|
+
"types": "./src/types/global.d.ts",
|
|
46
|
+
"exports": {
|
|
47
|
+
".": {
|
|
48
|
+
"types": "./src/types/global.d.ts",
|
|
49
|
+
"import": "./dist/es/ns.es.js",
|
|
50
|
+
"require": "./dist/cjs/ns.cjs.js",
|
|
51
|
+
"default": "./dist/es/ns.es.js"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"source": "./src/composables/ns.ts",
|
|
55
|
+
"unpkg": "./dist/iife/ns.iife.js",
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@eslint/js": "^9.28.0",
|
|
58
|
+
"@types/node": "^22.15.29",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
|
60
|
+
"@typescript-eslint/parser": "^8.33.0",
|
|
61
|
+
"@vitest/coverage-v8": "3.1.4",
|
|
62
|
+
"cross-env": "^7.0.3",
|
|
63
|
+
"dotenv": "^16.5.0",
|
|
64
|
+
"eslint": "^9.28.0",
|
|
65
|
+
"eslint-config-prettier": "^10.1.5",
|
|
66
|
+
"globals": "^16.2.0",
|
|
67
|
+
"jsdom": "^26.1.0",
|
|
68
|
+
"prettier": "3.5.3",
|
|
69
|
+
"typescript": "^5.8.3",
|
|
70
|
+
"typescript-eslint": "^8.33.0",
|
|
71
|
+
"vite": "^6.3.5"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"vitest": "^3.1.4"
|
|
75
|
+
}
|
|
90
76
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import style from '@/style/ns.css?raw'
|
|
2
|
+
import type { INanosplash } from '@/interfaces/INanosplash.ts'
|
|
3
|
+
import type { Func } from '@/types/generic/function.ts'
|
|
4
|
+
import type { INSElement } from '@/interfaces/INSElement.ts'
|
|
5
|
+
import {
|
|
6
|
+
all,
|
|
7
|
+
bod,
|
|
8
|
+
div,
|
|
9
|
+
doc,
|
|
10
|
+
first,
|
|
11
|
+
parseHtml,
|
|
12
|
+
parseRef,
|
|
13
|
+
toArray,
|
|
14
|
+
} from '@/utils/dom-utils.ts'
|
|
15
|
+
import { ClassNames, Selectors } from '@/constants/ns.ts'
|
|
16
|
+
import type { HTMLString } from '@/types/semantic/string.ts'
|
|
17
|
+
import type { ElementRef } from '@/types/dom.ts'
|
|
18
|
+
import type { int } from '@/types/semantic/number.ts'
|
|
19
|
+
import { version } from '@/../package.json'
|
|
20
|
+
|
|
21
|
+
export const useNs: Func<INanosplash> = (): INanosplash => {
|
|
22
|
+
/**
|
|
23
|
+
* Retrieve all NS elements from the DOM
|
|
24
|
+
*/
|
|
25
|
+
const getAllNs: Func<INSElement[]> = () =>
|
|
26
|
+
all(doc(), Selectors.ns) as INSElement[]
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create an NS element
|
|
30
|
+
*/
|
|
31
|
+
const makeNs: Func<INSElement> = () => {
|
|
32
|
+
const circle: HTMLString =
|
|
33
|
+
'<circle class=path cx=25 cy=25 r=20 fill=none />'
|
|
34
|
+
const svg = parseHtml(`<svg viewBox="0 0 50 50">${circle}</svg>`) as Element
|
|
35
|
+
const node = div(
|
|
36
|
+
ClassNames.ns,
|
|
37
|
+
div(ClassNames.nsText),
|
|
38
|
+
div(ClassNames.nsSpinner, svg)
|
|
39
|
+
) as INSElement
|
|
40
|
+
node.nsId = Date.now()
|
|
41
|
+
return node
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get all NS elements from DOM in a FIFO sequence
|
|
46
|
+
*/
|
|
47
|
+
const nsQueue: Func<INSElement[]> = () =>
|
|
48
|
+
getAllNs().sort((a, b) => a.nsId - b.nsId)
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Peek at the first NS element in the queue
|
|
52
|
+
*/
|
|
53
|
+
const peekNsQueue: Func<INSElement | null> = () => nsQueue()[0] ?? null
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Set the text of the NS element.
|
|
57
|
+
* @param ns Nanosplash element.
|
|
58
|
+
* @param text The text to display adjacent to the spinner.
|
|
59
|
+
*/
|
|
60
|
+
const setNsText = (ns: INSElement, text?: string): void => {
|
|
61
|
+
first(ns, Selectors.nsText)?.remove()
|
|
62
|
+
if (!text) return
|
|
63
|
+
const newNsText: Element = div(ClassNames.nsText, text)
|
|
64
|
+
ns.insertBefore(newNsText, ns.firstChild)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Set the parent element of the NS element.
|
|
69
|
+
* @param ns Nanosplash element.
|
|
70
|
+
* @param parent An element to be a parent of the Nanosplash element.
|
|
71
|
+
*/
|
|
72
|
+
const setNsParent = (ns: INSElement, parent: Element): void => {
|
|
73
|
+
const child = parent.firstElementChild
|
|
74
|
+
if (child) parent.insertBefore(ns, child)
|
|
75
|
+
parent.append(ns)
|
|
76
|
+
parent.classList.add(ClassNames.nsHost)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const getNsInside = (parent: Element): INSElement | undefined => {
|
|
80
|
+
const children = toArray(parent.children) as INSElement[]
|
|
81
|
+
return children.find((v: Element): boolean =>
|
|
82
|
+
v.classList.contains(ClassNames.ns)
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Show the Nanosplash in the window
|
|
88
|
+
* @param text The text that will appear adjacent to the spinner. Enter false to only show the spinner.
|
|
89
|
+
* @param inside Element or selector to an element wherein the Nanosplash element should reside.
|
|
90
|
+
*/
|
|
91
|
+
const show = (text?: string, inside?: ElementRef): int | null => {
|
|
92
|
+
const parent: Element = inside ? (parseRef(inside) ?? bod()) : bod()
|
|
93
|
+
let ns: INSElement
|
|
94
|
+
const recycled = getNsInside(parent)
|
|
95
|
+
if (recycled) {
|
|
96
|
+
ns = recycled as INSElement
|
|
97
|
+
} else {
|
|
98
|
+
ns = makeNs()
|
|
99
|
+
setNsParent(ns, parent)
|
|
100
|
+
}
|
|
101
|
+
setNsText(ns, text ?? '')
|
|
102
|
+
if (parent === bod()) {
|
|
103
|
+
const top: string = scrollY + 'px'
|
|
104
|
+
bod().style.setProperty('--ns-top', top)
|
|
105
|
+
}
|
|
106
|
+
return ns.nsId
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Remove a Nanosplash element from the DOM
|
|
111
|
+
* @param ns The Nanosplash element
|
|
112
|
+
*/
|
|
113
|
+
const removeNs = (ns?: Element | INSElement | null): void => {
|
|
114
|
+
ns?.parentElement?.classList.remove(ClassNames.nsHost)
|
|
115
|
+
ns?.remove()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Find a Nanosplash element with a given ID
|
|
120
|
+
* @param id The Nanosplash ID
|
|
121
|
+
*/
|
|
122
|
+
const findNs = (id: int): INSElement | null =>
|
|
123
|
+
getAllNs().find(x => x.nsId === id) ?? null
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Hide agnostically in FIFO order, specifically by ID, or all, by the asterisk symbol.
|
|
127
|
+
* The latter will delete all Nanosplash elements in the DOM.
|
|
128
|
+
* @param id Optional ID of a Nanosplash element or '*'.
|
|
129
|
+
*/
|
|
130
|
+
const hide = (id?: int | '*'): void => {
|
|
131
|
+
const selectAll = id === '*'
|
|
132
|
+
if (selectAll) getAllNs().forEach(removeNs)
|
|
133
|
+
else removeNs(typeof id === 'number' ? findNs(id) : peekNsQueue())
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Remove old Nanosplash styles and insert new ones.
|
|
138
|
+
*/
|
|
139
|
+
const injectStyle = () => {
|
|
140
|
+
first(doc(), '#ns')?.remove()
|
|
141
|
+
const styleElement: HTMLStyleElement = parseHtml(
|
|
142
|
+
`<style id="ns">${style}</style>`
|
|
143
|
+
)
|
|
144
|
+
bod().append(styleElement)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
injectStyle() // Run everytime the hook is invoked.
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
show,
|
|
151
|
+
hide,
|
|
152
|
+
version,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const ClassNames = {
|
|
2
|
+
ns: 'ns',
|
|
3
|
+
nsHost: 'nsh',
|
|
4
|
+
nsText: 'nst',
|
|
5
|
+
nsSpinner: 'nss',
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Selectors = {
|
|
9
|
+
ns: '.' + ClassNames.ns,
|
|
10
|
+
nsHost: '.' + ClassNames.nsHost,
|
|
11
|
+
nsText: '.' + ClassNames.nsText,
|
|
12
|
+
nsSpinner: '.' + ClassNames.nsSpinner,
|
|
13
|
+
}
|
package/src/iife.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ElementRef } from '@/types/dom.ts'
|
|
2
|
+
import type { int } from '@/types/semantic/number.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* # Nanosplash
|
|
6
|
+
* @author Isak Hauge <isakhauge@icloud.com>
|
|
7
|
+
*/
|
|
8
|
+
export interface INanosplash {
|
|
9
|
+
/**
|
|
10
|
+
* # 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.
|
|
15
|
+
* @example
|
|
16
|
+
* // Show spinner only
|
|
17
|
+
* ns.show()
|
|
18
|
+
* @example
|
|
19
|
+
* // Show spinner and text
|
|
20
|
+
* ns.show('Loading ...')
|
|
21
|
+
* @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')
|
|
32
|
+
* @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')
|
|
43
|
+
*/
|
|
44
|
+
show(
|
|
45
|
+
text?: string | null | undefined,
|
|
46
|
+
inside?: ElementRef | HTMLElement
|
|
47
|
+
): int | null
|
|
48
|
+
/**
|
|
49
|
+
* # 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
|
|
63
|
+
* @example
|
|
64
|
+
* // Hide all Nanosplash elements in the DOM
|
|
65
|
+
* ns.hide('*')
|
|
66
|
+
*/
|
|
67
|
+
hide(id?: int | '*'): void
|
|
68
|
+
/**
|
|
69
|
+
* # Version
|
|
70
|
+
* The current version of Nanosplash.
|
|
71
|
+
*/
|
|
72
|
+
version: string
|
|
73
|
+
}
|
package/src/style/ns.css
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
@keyframes nsRotate {
|
|
2
|
+
100% {
|
|
3
|
+
transform: rotate(360deg);
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@keyframes nsDash {
|
|
8
|
+
0% {
|
|
9
|
+
stroke-dasharray: 1, 150;
|
|
10
|
+
stroke-dashoffset: 0;
|
|
11
|
+
}
|
|
12
|
+
50% {
|
|
13
|
+
stroke-dasharray: 90, 150;
|
|
14
|
+
stroke-dashoffset: -35;
|
|
15
|
+
}
|
|
16
|
+
100% {
|
|
17
|
+
stroke-dasharray: 90, 150;
|
|
18
|
+
stroke-dashoffset: -124;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@keyframes nsFade {
|
|
23
|
+
from {
|
|
24
|
+
opacity: 0;
|
|
25
|
+
}
|
|
26
|
+
to {
|
|
27
|
+
opacity: 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@keyframes nsAscend {
|
|
32
|
+
from {
|
|
33
|
+
opacity: 0;
|
|
34
|
+
transform: translateY(13px);
|
|
35
|
+
}
|
|
36
|
+
to {
|
|
37
|
+
opacity: 1;
|
|
38
|
+
transform: translateY(0);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:root {
|
|
43
|
+
--ns-top: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Nanosplash host */
|
|
47
|
+
.nsh {
|
|
48
|
+
--color: DarkSlateGray;
|
|
49
|
+
--size: 20px;
|
|
50
|
+
--relSize: calc(var(--size) * 0.9);
|
|
51
|
+
--font: 'Inter', 'Helvetica', 'Arial';
|
|
52
|
+
--weight: 400;
|
|
53
|
+
--bg: rgba(255, 255, 255, 0.9);
|
|
54
|
+
--zIdx: 9999999999;
|
|
55
|
+
--blur: blur(5px);
|
|
56
|
+
|
|
57
|
+
position: relative;
|
|
58
|
+
z-index: var(--zIdx);
|
|
59
|
+
&::before {
|
|
60
|
+
width: 100%;
|
|
61
|
+
height: 100%;
|
|
62
|
+
bottom: 0; /* Not sure why */
|
|
63
|
+
right: 0; /* Not sure why */
|
|
64
|
+
position: absolute;
|
|
65
|
+
content: '';
|
|
66
|
+
background-color: var(--bg);
|
|
67
|
+
backdrop-filter: var(--blur);
|
|
68
|
+
-webkit-backdrop-filter: var(--blur);
|
|
69
|
+
z-index: calc(var(--zIdx) + 1);
|
|
70
|
+
border-radius: var(--size);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* For when the Nanosplash host is the document body */
|
|
75
|
+
body.nsh {
|
|
76
|
+
&,
|
|
77
|
+
&::before {
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 100%;
|
|
80
|
+
position: fixed;
|
|
81
|
+
top: var(--ns-top);
|
|
82
|
+
left: 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Nanosplash main element */
|
|
87
|
+
.ns {
|
|
88
|
+
width: 100%;
|
|
89
|
+
height: 100%;
|
|
90
|
+
bottom: 0; /* Not sure why */
|
|
91
|
+
right: 0; /* Not sure why */
|
|
92
|
+
display: flex;
|
|
93
|
+
justify-content: center;
|
|
94
|
+
align-items: center;
|
|
95
|
+
position: absolute;
|
|
96
|
+
z-index: calc(var(--zIdx) + 2);
|
|
97
|
+
animation: nsFade 2s;
|
|
98
|
+
gap: var(--relSize);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Nanosplash text */
|
|
102
|
+
.nst {
|
|
103
|
+
color: var(--color);
|
|
104
|
+
font-size: var(--size);
|
|
105
|
+
font-family: var(--font), sans-serif;
|
|
106
|
+
font-weight: var(--weight);
|
|
107
|
+
max-width: 80dvw;
|
|
108
|
+
white-space: nowrap;
|
|
109
|
+
text-overflow: ellipsis;
|
|
110
|
+
overflow: hidden;
|
|
111
|
+
text-shadow: 0 0 0.06rem rgba(0, 0, 0, 0.25);
|
|
112
|
+
filter: drop-shadow(0 0 0.07rem rgba(0, 0, 0, 0.25));
|
|
113
|
+
animation: nsAscend 0.5s;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Nanosplash spinner */
|
|
117
|
+
.nss {
|
|
118
|
+
display: block;
|
|
119
|
+
width: var(--relSize);
|
|
120
|
+
height: var(--relSize);
|
|
121
|
+
animation: nsAscend 0.5s;
|
|
122
|
+
& > svg {
|
|
123
|
+
animation: nsRotate 2s linear infinite;
|
|
124
|
+
position: relative;
|
|
125
|
+
width: inherit;
|
|
126
|
+
height: inherit;
|
|
127
|
+
stroke-width: 8;
|
|
128
|
+
}
|
|
129
|
+
& .path {
|
|
130
|
+
stroke: var(--color);
|
|
131
|
+
stroke-linecap: round;
|
|
132
|
+
animation: nsDash 1.5s ease-in-out infinite;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Func<T> = (...args: unknown[]) => T
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { INanosplash } from '../interfaces/INanosplash.ts'
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
interface Window {
|
|
5
|
+
ns: INanosplash
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* # Nanosplash
|
|
11
|
+
*
|
|
12
|
+
* **The tiny loading screen for web artisans**
|
|
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.
|
|
17
|
+
* @returns Instance of Nanosplash API
|
|
18
|
+
* @example The short and easy way.
|
|
19
|
+
* window.ns = useNs()
|
|
20
|
+
* @example The safe way.
|
|
21
|
+
* window.addEventListener('load', function() {
|
|
22
|
+
* this.ns = useNs()
|
|
23
|
+
* })
|
|
24
|
+
* @author Isak Hauge <https://www.linkedin.com/in/isakhauge/>
|
|
25
|
+
*/
|
|
26
|
+
export const useNs: () => INanosplash
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type int = number
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Func } from '@/types/generic/function.ts'
|
|
2
|
+
import type { HTMLString, NodeSelector } from '@/types/semantic/string.ts'
|
|
3
|
+
import type { ElementRef } from '@/types/dom.ts'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shortened Document getter
|
|
7
|
+
*/
|
|
8
|
+
export const doc: Func<Document> = (): Document => globalThis.document
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Shortened Body getter
|
|
12
|
+
*/
|
|
13
|
+
export const bod: Func<HTMLElement> = (): HTMLElement =>
|
|
14
|
+
globalThis.document.body
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Convert iterables and array-like structures into an array
|
|
18
|
+
* @param x
|
|
19
|
+
*/
|
|
20
|
+
export const toArray = <T>(x: Iterable<T> | ArrayLike<T>) => Array.from(x)
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Scoped node selector
|
|
24
|
+
* @param node Current scope
|
|
25
|
+
* @param ref Selector
|
|
26
|
+
*/
|
|
27
|
+
export const all = (node: ParentNode, ref: NodeSelector) =>
|
|
28
|
+
toArray(node.querySelectorAll(ref))
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Scoped node selector returning the first match
|
|
32
|
+
* @param node Current scope
|
|
33
|
+
* @param ref Selector
|
|
34
|
+
*/
|
|
35
|
+
export const first = (node: ParentNode, ref: NodeSelector): Element | null =>
|
|
36
|
+
all(node, ref)[0] ?? null
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parse an ambiguous element reference into an actual element
|
|
40
|
+
* @param ref Element reference
|
|
41
|
+
*/
|
|
42
|
+
export const parseRef = <T extends Element>(ref: ElementRef): T | null =>
|
|
43
|
+
ref instanceof Element ? (ref as T) : (first(doc(), ref) as T)
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create div
|
|
47
|
+
* @param className Optional CSS class name
|
|
48
|
+
* @param children Optional children
|
|
49
|
+
*/
|
|
50
|
+
export const div = (
|
|
51
|
+
className?: string,
|
|
52
|
+
...children: (Node | HTMLString)[]
|
|
53
|
+
): HTMLDivElement => {
|
|
54
|
+
const node: HTMLDivElement = doc().createElement('div')
|
|
55
|
+
if (className) node.classList.add(className)
|
|
56
|
+
node.append(...children)
|
|
57
|
+
return node
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse HTML code into HTML elements
|
|
62
|
+
* @param html
|
|
63
|
+
*/
|
|
64
|
+
export const parseHtml = <T extends HTMLElement>(html: HTMLString): T => {
|
|
65
|
+
const node: HTMLDivElement = div()
|
|
66
|
+
node.innerHTML = html
|
|
67
|
+
return node.firstChild as T
|
|
68
|
+
}
|