nanosplash 2.2.4 → 2.3.0

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 (52) hide show
  1. package/.github/workflows/production.yml +57 -0
  2. package/.github/workflows/release.yml +61 -0
  3. package/README.md +17 -15
  4. package/dist/iife/nanosplash.iife.js +1 -1
  5. package/dist/module/Nanosplash.css +1 -0
  6. package/dist/module/manifest.json +6 -3
  7. package/dist/module/nanosplash.cjs.js +1 -1
  8. package/dist/module/nanosplash.es.js +244 -330
  9. package/docs/index.html +54 -430
  10. package/docs/nanosplash.iife.js +1 -1
  11. package/docs/style.css +1 -1
  12. package/docs/typedoc/assets/highlight.css +1 -50
  13. package/docs/typedoc/assets/main.js +2 -2
  14. package/docs/typedoc/assets/search.js +1 -1
  15. package/docs/typedoc/assets/style.css +32 -2
  16. package/docs/typedoc/classes/Core_Nanosplash.Nanosplash.html +1 -0
  17. package/docs/typedoc/index.html +1 -1
  18. package/docs/typedoc/modules/types.html +1 -1
  19. package/index.html +51 -412
  20. package/jest.config.ts +194 -0
  21. package/package.json +70 -65
  22. package/src/Core/Nanosplash.ts +74 -0
  23. package/src/Core/SplashInstance.ts +154 -0
  24. package/src/Factory/NanosplashFactory.ts +73 -0
  25. package/src/Interfaces/ContextualAPIInterface.ts +8 -0
  26. package/src/Interfaces/IdentityInterface.ts +3 -0
  27. package/src/Interfaces/ImageInterface.ts +5 -0
  28. package/src/Interfaces/InsideInterface.ts +5 -0
  29. package/src/Interfaces/NanosplashInterface.ts +4 -0
  30. package/src/Interfaces/ProgressInterface.ts +5 -0
  31. package/src/Interfaces/ShowInterface.ts +5 -0
  32. package/src/Interfaces/WhileInterface.ts +5 -0
  33. package/src/main.ts +2 -6
  34. package/src/repositories/NanosplashRepository.ts +34 -55
  35. package/src/style.sass +36 -61
  36. package/src/types.d.ts +45 -200
  37. package/src/utilities/dom.spec.ts +70 -0
  38. package/src/utilities/dom.ts +17 -256
  39. package/update-all.sh +3 -0
  40. package/vite.iife.config.js +46 -18
  41. package/vite.mod.config.js +48 -12
  42. package/vite.site.config.js +0 -3
  43. package/dist/module/style.css +0 -1
  44. package/docs/typedoc/classes/Nanosplash.Nanosplash-1.html +0 -56
  45. package/docs/typedoc/classes/types.Nanosplash.html +0 -56
  46. package/docs/typedoc/modules/Nanosplash.html +0 -1
  47. package/src/Exceptions/Exception.ts +0 -12
  48. package/src/Exceptions/IllegalArgumentException.ts +0 -9
  49. package/src/Exceptions/InvalidDestinationException.ts +0 -9
  50. package/src/Exceptions/MissingResourceException.ts +0 -9
  51. package/src/Nanosplash.ts +0 -484
  52. package/update-latest.sh +0 -21
package/src/style.sass CHANGED
@@ -1,68 +1,43 @@
1
- // Keyframe animations:
2
- @keyframes pulse
3
- from
4
- transform: scale(1)
5
- to
6
- transform: scale(1.1)
1
+ $z-index-regular: 1
2
+ $z-index-fullscreen: 2
3
+ $bg-color: rgba(White, 0.8)
7
4
 
8
- @keyframes spin
9
- from
10
- transform: rotate(0deg)
11
- to
12
- transform: rotate(360deg)
5
+ %blur
6
+ filter: blur(5px)
7
+ overflow: hidden
13
8
 
14
- .nanosplash-container
15
- // Base of container
16
- position: absolute
17
- flex-direction: column
18
- justify-content: center
19
- align-items: center
20
- z-index: 1
21
- border-radius: inherit
22
- overflow: inherit
9
+ .ns-wrapper
10
+ position: relative
23
11
 
24
- // When container is hidden
25
- &[hidden="true"]
26
- display: none
27
- // When container is visible
28
- &:not([hidden="true"])
29
- display: flex
12
+ .ns-fs
13
+ top: 0
14
+ left: 0
15
+ position: fixed
16
+ min-width: 100%
17
+ min-height: 100vh
18
+ z-index: $z-index-fullscreen
30
19
 
31
- // Standardized blur modes:
32
- &[data-blur="none"]:not([hidden="true"]) ~ *
33
- filter: blur(0)
34
- &[data-blur="light"]:not([hidden="true"]) ~ *
35
- filter: blur(2px)
36
- &[data-blur="medium"]:not([hidden="true"]) ~ *
37
- filter: blur(4px)
38
- &[data-blur="heavy"]:not([hidden="true"]) ~ *
39
- filter: blur(10px)
20
+ // Fullscreen content blur
21
+ body .ns-fs ~ *, .ns-blur
22
+ @extend %blur
40
23
 
41
- // Loading text
42
- .nanosplash-text
43
- text-rendering: optimizeLegibility
44
- -moz-osx-font-smoothing: grayscale
45
- font-smoothing: antialiased
46
- -webkit-font-smoothing: antialiased
47
- text-shadow: 0 0 2px rgba(0, 0, 0, 0.2)
48
-
49
- // Splash image
50
- img.nanosplash-img
51
- margin-bottom: 40px
24
+ .ns-window
25
+ top: 0
26
+ left: 0
27
+ width: 100%
28
+ height: 100%
29
+ display: flex
30
+ position: absolute
31
+ align-items: center
32
+ justify-content: center
33
+ z-index: $z-index-regular
34
+ background-color: $bg-color
52
35
 
53
- // Standardized splash image animations:
54
- &[data-splash-animation="pulse"] > img
55
- animation-name: pulse
56
- animation-duration: 300ms
57
- animation-direction: alternate
58
- animation-iteration-count: infinite
59
- &[data-splash-animation="spin"] > img
60
- animation-name: spin
61
- animation-duration: 700ms
62
- animation-iteration-count: infinite
63
- animation-timing-function: linear
36
+ .ns-img
37
+ max-width: 9rem
38
+ max-height: 9rem
39
+ margin-bottom: 1rem
64
40
 
65
- // Style exception when container is the direct child of body
66
- body > &:not([hidden="true"])
67
- height: 100vh
68
- width: 100%
41
+ .ns-text
42
+ color: #333
43
+ text-align: center
package/src/types.d.ts CHANGED
@@ -1,205 +1,50 @@
1
- declare module 'nanosplash' {
2
- /**
3
- * Nanosplash
4
- *
5
- * @author Isak K. Hauge
6
- */
7
- class Nanosplash {
8
- /**
9
- * Constructor
10
- *
11
- * @public
12
- * @constructor
13
- * @param {Config | undefined} config The configuration object.
14
- *
15
- * @example
16
- * ```js
17
- * // Instantiatie without config object.
18
- * new Nanosplash()
19
- * ```
20
- *
21
- * @example
22
- * ```js
23
- * // Instantiate with config object.
24
- * new Nanosplash(config)
25
- * ```
26
- */
27
- constructor(config?: Config)
28
-
29
- /**
30
- * Configure
31
- *
32
- * @param {Config} config The config object.
33
- * @return {Nanosplash} The updated instance.
34
- *
35
- * @description Mutates the existing configuration and returns the updated
36
- * instance.
37
- *
38
- * @example
39
- * ```js
40
- * nanosplash.configure(config)
41
- * ```
42
- *
43
- * @example
44
- * ```js
45
- * window.loading.configure(config).show(text)
46
- * ```
47
- */
48
- configure(config: Config): Nanosplash
49
-
50
- /**
51
- * Show
52
- *
53
- * @param {string | undefined} text The text that will be shown in the
54
- * loading screen. If undefined, Nanosplash will use the default text.
55
- * @return {DisplayController} An object with a function that controls the
56
- * visibility and destination of the loading screen.
57
- *
58
- * @description Shows the loading screen. Invoking the inside function will
59
- * display the loading screen within the constraints of the defined
60
- * destination.
61
- *
62
- * Basic usage.
63
- * @example
64
- * ```js
65
- * // Basic usage.
66
- * loading.show(text)
67
- * ```
68
- *
69
- * Display Nanosplash inside other DOM elements.
70
- * @example
71
- * ```js
72
- * // Show loading screen within another element using CSS selector.
73
- * loading.show(text).inside('#my-element')
74
- * // Use DOM element reference.
75
- * loading.show(text).inside(document.getElementById(id))
76
- * // Use function that returns a DOM element reference.
77
- * loading.show(text).inside(() => getDomReference())
78
- * ```
79
- *
80
- * Display Nanosplash while an asynchronous task is resolving.
81
- * @example
82
- * ```js
83
- * // Define an asynchronous task.
84
- * const task = async () => await getDataFromApi()
85
- * // Show loading screen while the task is running.
86
- * loading.show(text).during(task)
87
- * loading.show(text).inside('#my-element').during(task)
88
- * ```
89
- */
90
- show(text?: string): DisplayController
91
-
92
- /**
93
- * Hide
94
- *
95
- * @description Hides the loading screen and moves to its default destination.
96
- * If no custom default destination is defined, it will move to the document
97
- * body.
98
- *
99
- * @example
100
- * ```js
101
- * // Basic usage
102
- * loading.hide()
103
- * // Use with async functions
104
- * loading.show('Loading ...')
105
- * await fetchStuffFromApi()
106
- * loading.hide()
107
- * ```
108
- */
109
- hide(): void
110
-
111
- /**
112
- * Install
113
- *
114
- * @description Assigns itself to the Window object and sets essential event
115
- * listeners that responds to changes in the browser. The instance is
116
- * globally accessible through the property "loading".
117
- *
118
- * @example
119
- * ```js
120
- * // Install
121
- * (new Nanosplash()).install()
122
- *
123
- * // Access the instance globally
124
- * loading.show('Some text')
125
- * ```
126
- */
127
- public install(): void
128
- }
129
-
130
- type SplashAnimation = 'none' | 'pulse' | 'spin'
131
-
132
- type DefaultOptions = {
133
- destination?: Destination
134
- text?: string
135
- }
136
-
137
- type TextOptions = {
138
- family?: string
139
- weight?: string
140
- color?: string
141
- size?: string
142
- }
143
-
144
- type SplashOptions = {
145
- src?: string
146
- width?: string
147
- height?: string
148
- animation?: SplashAnimation
149
- }
150
-
151
- type BlurMode = 'none' | 'light' | 'medium' | 'heavy'
1
+ import {Nanosplash} from "./Core/Nanosplash";
2
+ import {ShowInterface} from "./Interfaces/ShowInterface";
3
+ import {InsideInterface} from "./Interfaces/InsideInterface";
4
+ import {SplashInstance} from "./Core/SplashInstance";
5
+ import {ContextualAPIInterface} from "./Interfaces/ContextualAPIInterface";
152
6
 
153
- type BackgroundOptions = {
154
- color?: string
155
- blur?: BlurMode
156
- }
157
-
158
- type Destination = Node | Element | DestinationCallback | string
159
-
160
- type DestinationCallback = () => Element
161
-
162
- type Config = {
163
- default?: DefaultOptions
164
- text?: TextOptions
165
- splash?: SplashOptions
166
- background?: BackgroundOptions
167
- }
168
-
169
- type TargetNodeController<T> = (target: T, asFirstChild?: boolean) => void
170
- type TargetNodeObject<T> = {
171
- [key: string]: TargetNodeController<T>
172
- }
173
- type TaskController<T> = (task: Promise<T>) => Promise<void>
174
-
175
- type DisplayController = {
176
- during: (task: Promise<any>) => Promise<void>
177
- inside: (destination: Destination) => {
178
- during: (task: Promise<any>) => Promise<void>
179
- }
180
- }
181
-
182
- /**
183
- * @internal
184
- */
185
- type MakeOptions = {
186
- id?: string
187
- className?: string
188
- attributes?: { key: string; value: string | null }[]
189
- eventListeners?: {
190
- event: keyof HTMLElementEventMap
191
- handler: EventListener
192
- }[]
193
- content?: Node | Element | HTMLElement | string
7
+ declare global {
8
+ interface Window {
9
+ ns: Nanosplash
194
10
  }
11
+ }
195
12
 
196
- /**
197
- * @internal
198
- */
199
- type HTMLElementTag = keyof HTMLElementTagNameMap
13
+ type Destination = Node | string
14
+ type SplashJob = [Promise<any>, string]
15
+
16
+ /**
17
+ * @internal
18
+ */
19
+ export type HTMLElementTag = keyof HTMLElementTagNameMap
20
+
21
+ /**
22
+ * @internal
23
+ */
24
+ type CSSProperty = keyof CSSStyleDeclaration
25
+
26
+ type ShowFunction = (text: string) => ContextualAPIInterface
27
+ type ProgressFunction = (...[]: SplashJob[]) => ContextualAPIInterface
28
+ type WhileFunction = (asyncTask: Promise<any>) => ShowInterface
29
+ type StrategyObject = {
30
+ show: ShowFunction,
31
+ progress: ProgressFunction,
32
+ while: WhileFunction
33
+ }
34
+ type ImgFunction = (src: string) => StrategyObject
35
+ type InsideFunction = (selector: string) => ContextualAPIInterface
36
+ type ContextualAPIObject = {
37
+ getId: () => string,
38
+ inside?: InsideFunction,
39
+ remove: () => void
40
+ moveTo: (selector: string) => void,
41
+ getText: () => string,
42
+ setText: (text: string) => SplashInstance,
43
+ getImgSrc: () => string | undefined,
44
+ setImgSrc: (src?: string) => SplashInstance
45
+ }
200
46
 
201
- /**
202
- * @internal
203
- */
204
- type CSSProperty = keyof CSSStyleDeclaration
47
+ declare module 'nanosplash' {
48
+ class Nanosplash {}
49
+ class SplashInstance {}
205
50
  }
@@ -0,0 +1,70 @@
1
+ import {addClass, get, mk, move, setAttr} from "./dom";
2
+
3
+ const resetDOM = () => document.body.innerHTML = ''
4
+
5
+ test('Single DOM get function', () => {
6
+ const element = document.createElement('div')
7
+ element.id = 'a'
8
+ element.classList.add('b')
9
+ element.setAttribute('data-ref', 'c')
10
+ document.body.append(element)
11
+
12
+ const selector = 'div#a.b[data-ref="c"]'
13
+ const refElement = get<HTMLDivElement>(selector)
14
+
15
+ expect(refElement?.outerHTML).toBe(element.outerHTML)
16
+
17
+ resetDOM()
18
+ })
19
+
20
+ test('Move Element from origin to destination', () => {
21
+ const html = `
22
+ <div id="main">
23
+ <div id="a">
24
+ <div id="b">
25
+ <div id="c">
26
+ <div id="first"></div>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ `
32
+ document.body.innerHTML = html
33
+
34
+ const main = get('#main') as HTMLDivElement
35
+ const first = get('#first') as HTMLDivElement
36
+ const b = get('#b') as HTMLDivElement
37
+ const c = get('#c') as HTMLDivElement
38
+
39
+ move(b, main)
40
+ move(c, main)
41
+ move(first, main, true)
42
+
43
+ const ids = Array.from(main.children).map((v => v.id))
44
+
45
+ expect(ids.join(',')).toBe('first,a,b,c')
46
+
47
+ resetDOM()
48
+ })
49
+
50
+ test('Make HTML Element', () => {
51
+ document.body.appendChild(mk('span'))
52
+ const span = document.body.children.item(0) as Node
53
+ expect(span instanceof HTMLSpanElement).toBe(true)
54
+ resetDOM()
55
+ })
56
+
57
+ test('Add CSS classes', () => {
58
+ const div = mk('div')
59
+ addClass(div, 'ns-1', 'ns-2', 'ns-3')
60
+ expect(div.classList.toString()).toBe('ns-1 ns-2 ns-3')
61
+ resetDOM()
62
+ })
63
+
64
+ test('Set Node attribute', () => {
65
+ const div = mk('div')
66
+ const attr = {name: 'data-test', value: 'success'}
67
+ setAttr(div, attr.name, attr.value)
68
+ expect(div.getAttribute(attr.name)).toBe(attr.value)
69
+ resetDOM()
70
+ })
@@ -1,265 +1,26 @@
1
- import {
2
- CSSProperty,
3
- HTMLElementTag,
4
- MakeOptions,
5
- TargetNodeObject,
6
- } from 'nanosplash'
1
+ import {HTMLElementTag} from "../types";
7
2
 
8
- /**
9
- * Ref (reference)
10
- *
11
- * @param {string} cssSelector
12
- * @return {Element | null}
13
- *
14
- * @description Returns the instance reference of whatever element in the DOM
15
- * that matches the CSS selector string.
16
- */
17
- export const ref = (cssSelector: string): HTMLElement | null =>
18
- document.querySelector(cssSelector)
19
-
20
- /**
21
- * Ref All (reference)
22
- *
23
- * @param {string} cssSelector
24
- * @return {Element[]}
25
- *
26
- * @description Returns the instance references of whatever element in the DOM
27
- * that matches the CSS selector string.
28
- */
29
- export const refAll = (cssSelector: string): HTMLElement[] =>
30
- Array.from(document.querySelectorAll(cssSelector))
31
-
32
- /**
33
- * Create
34
- *
35
- * @param {HTMLElementTag} tag Valid HTML element tag name
36
- * @param {MakeOptions} options Make options
37
- * @returns {HTMLElement}
38
- *
39
- * @description Creates any HTML element that corresponds with the given tag.
40
- * Additionally, if options are defined, it can mutate the element by adding
41
- * class names, attributes, and content.
42
- */
43
- export const create = (
44
- tag: HTMLElementTag,
45
- options?: MakeOptions
46
- ): HTMLElement => {
47
- const element = document.createElement(tag)
48
- if (options) {
49
- // Element ID
50
- element.id ||= options.id + ''
51
-
52
- // Element class names
53
- element.className ||= options.className + ''
54
-
55
- // Element attributes
56
- options.attributes
57
- ?.filter(v => v.value)
58
- .forEach(({ key, value }) => setAttribute(element, key, value as string))
59
-
60
- // Element content
61
- if (options.content) {
62
- if (typeof options.content === 'string') {
63
- element.innerText = options.content
64
- } else {
65
- element.append(options.content)
66
- }
67
- }
68
- }
69
- return element
3
+ export function get<T>(selector: string): HTMLElement | T | null {
4
+ return document.querySelector(selector) as HTMLElement | T | null
70
5
  }
71
6
 
72
- /**
73
- * Display
74
- *
75
- * @param {HTMLElement} node The HTML element.
76
- * @param {boolean} show A boolean value that decides the node's visibility.
77
- */
78
- export const display = (node: HTMLElement, show: boolean): void => {
79
- node.hidden = !show
80
- if (show) {
81
- removeAttribute(node, 'hidden')
82
- } else {
83
- setAttribute(node, 'hidden', 'true')
84
- }
7
+ export function move(source: Node, destination: Node, asFirstChild: boolean = false): void
8
+ {
9
+ if (source && destination) {
10
+ (destination.hasChildNodes() && asFirstChild)
11
+ ? destination.insertBefore(source, destination.firstChild)
12
+ : destination.appendChild(source)
13
+ }
85
14
  }
86
15
 
87
- /**
88
- * Move
89
- *
90
- * @param {Node} node The node to append to another, as its child.
91
- * @returns An object with a target node controller function. Said function
92
- * will append the given node to a yet to be defined target node.
93
- *
94
- * @description This function will in turn append the node to another node.
95
- * If the child node already is a reference to another node in the
96
- * DOM, it will be moved to its new position by removing the current reference
97
- * before creating the new one.
98
- *
99
- * @example move(node).to(targetNode) // Regular append
100
- * @example mode(child).to(parent, true) // Append as first child
101
- */
102
- export const move = (node: Node): TargetNodeObject<Node> => ({
103
- /**
104
- * @param {Node} targetNode The node in which the new node shall reside.
105
- * @param {boolean} asFirstChild Whether or not the new node shall be the
106
- * first child of the target node.
107
- */
108
- to: (targetNode: Node, asFirstChild?: boolean): void => {
109
- const children = Array.from(targetNode.childNodes)
110
- const noChildren = children.length < 1
111
- if (noChildren || !asFirstChild) {
112
- targetNode.appendChild<Node>(node)
113
- return
114
- }
115
- const firstChild = children[0]
116
- targetNode.insertBefore(node, firstChild)
117
- },
118
- })
119
-
120
- /**
121
- * Fit Parent Dimensions
122
- *
123
- * @param {HTMLElement} node The HTML element of which style to change.
124
- *
125
- * @description Transforms the node's dimensions and document position in order
126
- * to match its parent node.
127
- */
128
- export const fitToParent = (node: HTMLElement): void => {
129
- const parent = node.parentNode as HTMLElement
130
- if (parent) {
131
- ;((domRect: DOMRect) => {
132
- const unit = 'px'
133
- const parentIsBody = parent === document.body
134
- let left, top, width, height
135
-
136
- // Set distance from left
137
- if (parentIsBody) {
138
- left = scrollX + unit
139
- } else {
140
- left = 0 + unit
141
- }
142
-
143
- // Set distance from top
144
- if (parentIsBody) {
145
- top = scrollY + unit
146
- } else {
147
- top = 0 + unit
148
- }
149
-
150
- // Set width from parent
151
- if (parentIsBody) {
152
- width = '100%'
153
- } else {
154
- width = domRect.width + unit
155
- }
156
-
157
- // Set height from parent
158
- if (parentIsBody) {
159
- height = '100vh'
160
- } else {
161
- height = domRect.height + unit
162
- }
163
-
164
- setStyle(node, 'left', left)
165
- setStyle(node, 'top', top)
166
- setStyle(node, 'width', width)
167
- setStyle(node, 'height', height)
168
- })(parent.getBoundingClientRect())
169
- }
170
- }
171
-
172
- declare global {
173
- interface Window {
174
- attachEvent: Function
175
- }
176
- interface Node {
177
- attachEvent: Function
178
- }
179
- }
180
-
181
- /**
182
- * Invoke On
183
- *
184
- * @param {Window | Node} dispatcher The node in which the given events
185
- * are captured.
186
- * @param {() => void} handler A function to be invoked when one of the given
187
- * events are captured.
188
- * @param {keyof WindowEventMap} events An array of event names.
189
- *
190
- * @example invokeOn(window, () => alert('Scroll'), ['scroll'])
191
- */
192
- export const invokeOn = (
193
- dispatcher: Window | Node,
194
- handler: () => void,
195
- events: Array<keyof WindowEventMap>
196
- ): void =>
197
- (([addEventListenerLegacy, addEventListener]) => {
198
- events.forEach(event => {
199
- if (addEventListenerLegacy) {
200
- addEventListenerLegacy(`on${event}`, handler)
201
- } else {
202
- addEventListener(event, handler, true)
203
- }
204
- })
205
- })([dispatcher.attachEvent, dispatcher.addEventListener])
206
-
207
- /**
208
- * Is Element or Node
209
- *
210
- * @param {any} value A value of any type.
211
- * @returns {boolean}
212
- */
213
- export const isElementOrNode = (value: any): boolean => {
214
- return value instanceof Element || value instanceof Node
215
- }
216
-
217
- /**
218
- * Is Function
219
- *
220
- * @param {any} value A value of any type.
221
- * @returns {boolean}
222
- */
223
- export const isFunction = (value: any): boolean => {
224
- return value && {}.toString.call(value) === '[object Function]'
225
- }
226
-
227
- /**
228
- * Set Style
229
- *
230
- * @param {HTMLElement} node An HTML element.
231
- * @param {CSSProperty} prop A CSS property.
232
- * @param {string} value A CSS value.
233
- *
234
- * @description Sets a CSS declaration on an HTML element.
235
- *
236
- * @example setStyle(divElement, 'color', '#FF0000')
237
- */
238
- export const setStyle = (
239
- node: HTMLElement,
240
- prop: CSSProperty,
241
- value: any
242
- ): void => {
243
- node.style[prop as any] = value
244
- }
16
+ export const mk = (tag: HTMLElementTag) => document.createElement(tag)
245
17
 
246
- /**
247
- * Set Attribute
248
- *
249
- * @param {Element} node An element.
250
- * @param {string} key The attribute name.
251
- * @param {string} value The attribute value.
252
- */
253
- export const setAttribute = (node: Element, key: string, value: string) => {
254
- node.setAttribute(key, value)
18
+ export function addClass(node: HTMLElement, ...classes: string[]): void
19
+ {
20
+ node.classList.add(...classes)
255
21
  }
256
22
 
257
- /**
258
- * Remove Attribute
259
- *
260
- * @param {Element} node An element.
261
- * @param {string} key The attribute name.
262
- */
263
- export const removeAttribute = (node: Element, key: string) => {
264
- node.removeAttribute(key)
23
+ export function setAttr(node: HTMLElement, attribute: string, value: string): void
24
+ {
25
+ node.setAttribute(attribute, value)
265
26
  }