@sv443-network/userutils 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +109 -23
- package/dist/index.global.js +443 -0
- package/dist/index.js +426 -27
- package/dist/index.mjs +400 -2
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/dom.d.ts +7 -2
- package/dist/lib/onSelector.d.ts +3 -3
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @sv443-network/userutils
|
|
2
2
|
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4799a9f: Fix TS error in ConfigManager migration functions
|
|
8
|
+
|
|
9
|
+
## 1.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- db5cded: Added `isScrollable()` to check whether an element has a horizontal or vertical scroll bar
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 9e26464: Replaced most occurrences of `HTMLElement` in the docs with `Element` for better compatibility
|
|
18
|
+
|
|
3
19
|
## 1.0.0
|
|
4
20
|
|
|
5
21
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## UserUtils
|
|
4
4
|
Zero-dependency library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more.
|
|
5
|
+
|
|
5
6
|
Contains builtin TypeScript declarations. Webpack compatible and supports ESM and CJS.
|
|
6
7
|
If you like using this library, please consider [supporting the development ❤️](https://github.com/sponsors/Sv443)
|
|
7
8
|
|
|
@@ -26,6 +27,7 @@ If you like using this library, please consider [supporting the development ❤
|
|
|
26
27
|
- [interceptEvent()](#interceptevent) - conditionally intercepts events registered by `addEventListener()` on any given EventTarget object
|
|
27
28
|
- [interceptWindowEvent()](#interceptwindowevent) - conditionally intercepts events registered by `addEventListener()` on the window object
|
|
28
29
|
- [amplifyMedia()](#amplifymedia) - amplify an audio or video element's volume past the maximum of 100%
|
|
30
|
+
- [isScrollable()](#isscrollable) - check if an element has a horizontal or vertical scroll bar
|
|
29
31
|
- [Math:](#math)
|
|
30
32
|
- [clamp()](#clamp) - constrain a number between a min and max value
|
|
31
33
|
- [mapRange()](#maprange) - map a number from one range to the same spot in another range
|
|
@@ -80,14 +82,13 @@ If you like using this library, please consider [supporting the development ❤
|
|
|
80
82
|
|
|
81
83
|
## Preamble:
|
|
82
84
|
This library is written in TypeScript and contains builtin TypeScript declarations.
|
|
83
|
-
The usages and examples in this readme are written in TypeScript, but the library can also be used in plain JavaScript.
|
|
84
|
-
|
|
85
|
-
Some features require the `@run-at` or `@grant` directives to be tweaked in the userscript header or have other requirements.
|
|
86
|
-
Their documentation will contain a section marked by a warning emoji (⚠️) that will go into more detail.
|
|
87
85
|
|
|
88
86
|
Each feature has example code that can be expanded by clicking on the text "Example - click to view".
|
|
87
|
+
The usages and examples are written in TypeScript, but the library can also be used in plain JavaScript after removing the type annotations (and changing the imports if you are using CommonJS).
|
|
88
|
+
If the usage section contains multiple definitions of the function, each occurrence represents an overload and you can choose which one you want to use.
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Some features require the `@run-at` or `@grant` directives to be tweaked in the userscript header or have other requirements.
|
|
91
|
+
Their documentation will contain a section marked by a warning emoji (⚠️) that will go into more detail.
|
|
91
92
|
|
|
92
93
|
<br><br>
|
|
93
94
|
|
|
@@ -123,6 +124,7 @@ If set to `false` (default), querySelector() will be used and only the first mat
|
|
|
123
124
|
If `continuous` is set to `true`, the listener will not be deregistered after it was called once (defaults to false).
|
|
124
125
|
|
|
125
126
|
When using TypeScript, the generic `TElement` can be used to specify the type of the element(s) that the listener will return.
|
|
127
|
+
It will default to `HTMLElement` if left undefined.
|
|
126
128
|
|
|
127
129
|
⚠️ In order to use this function, [`initOnSelector()`](#initonselector) has to be called as soon as possible.
|
|
128
130
|
This initialization function has to be called after `DOMContentLoaded` is fired (or immediately if `@run-at document-end` is set).
|
|
@@ -132,6 +134,8 @@ Calling onSelector() before `DOMContentLoaded` is fired will not throw an error,
|
|
|
132
134
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
133
135
|
|
|
134
136
|
```ts
|
|
137
|
+
import { initOnSelector, onSelector } from "@sv443-network/userutils";
|
|
138
|
+
|
|
135
139
|
document.addEventListener("DOMContentLoaded", initOnSelector);
|
|
136
140
|
|
|
137
141
|
// Continuously checks if `div` elements are added to the DOM, then returns all of them (even previously detected ones) in a NodeList
|
|
@@ -177,6 +181,8 @@ You may see all options [here](https://developer.mozilla.org/en-US/docs/Web/API/
|
|
|
177
181
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
178
182
|
|
|
179
183
|
```ts
|
|
184
|
+
import { initOnSelector } from "@sv443-network/userutils";
|
|
185
|
+
|
|
180
186
|
document.addEventListener("DOMContentLoaded", () => {
|
|
181
187
|
initOnSelector({
|
|
182
188
|
attributes: true,
|
|
@@ -201,6 +207,8 @@ Since multiple listeners can be registered for the same selector, the value of t
|
|
|
201
207
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
202
208
|
|
|
203
209
|
```ts
|
|
210
|
+
import { initOnSelector, onSelector, getSelectorMap } from "@sv443-network/userutils";
|
|
211
|
+
|
|
204
212
|
document.addEventListener("DOMContentLoaded", initOnSelector);
|
|
205
213
|
|
|
206
214
|
onSelector<HTMLDivElement>("div", {
|
|
@@ -244,6 +252,8 @@ Userscripts are sandboxed and do not have access to the regular window object, s
|
|
|
244
252
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
245
253
|
|
|
246
254
|
```ts
|
|
255
|
+
import { getUnsafeWindow } from "@sv443-network/userutils";
|
|
256
|
+
|
|
247
257
|
// trick the site into thinking the mouse was moved:
|
|
248
258
|
const mouseEvent = new MouseEvent("mousemove", {
|
|
249
259
|
view: getUnsafeWindow(),
|
|
@@ -252,6 +262,7 @@ const mouseEvent = new MouseEvent("mousemove", {
|
|
|
252
262
|
movementX: 10,
|
|
253
263
|
movementY: 0,
|
|
254
264
|
});
|
|
265
|
+
|
|
255
266
|
document.body.dispatchEvent(mouseEvent);
|
|
256
267
|
```
|
|
257
268
|
|
|
@@ -262,7 +273,7 @@ document.body.dispatchEvent(mouseEvent);
|
|
|
262
273
|
### insertAfter()
|
|
263
274
|
Usage:
|
|
264
275
|
```ts
|
|
265
|
-
insertAfter(beforeElement:
|
|
276
|
+
insertAfter(beforeElement: Element, afterElement: Element): Element
|
|
266
277
|
```
|
|
267
278
|
|
|
268
279
|
Inserts the element passed as `afterElement` as a sibling after the passed `beforeElement`.
|
|
@@ -273,10 +284,13 @@ The passed `afterElement` will be returned.
|
|
|
273
284
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
274
285
|
|
|
275
286
|
```ts
|
|
287
|
+
import { insertAfter } from "@sv443-network/userutils";
|
|
288
|
+
|
|
276
289
|
// insert a <div> as a sibling next to an element
|
|
277
290
|
const beforeElement = document.querySelector("#before");
|
|
278
291
|
const afterElement = document.createElement("div");
|
|
279
292
|
afterElement.innerText = "After";
|
|
293
|
+
|
|
280
294
|
insertAfter(beforeElement, afterElement);
|
|
281
295
|
```
|
|
282
296
|
|
|
@@ -287,7 +301,7 @@ insertAfter(beforeElement, afterElement);
|
|
|
287
301
|
### addParent()
|
|
288
302
|
Usage:
|
|
289
303
|
```ts
|
|
290
|
-
addParent(element:
|
|
304
|
+
addParent(element: Element, newParent: Element): Element
|
|
291
305
|
```
|
|
292
306
|
|
|
293
307
|
Adds a parent element around the passed `element` and returns the new parent.
|
|
@@ -298,10 +312,13 @@ Previously registered event listeners are kept intact.
|
|
|
298
312
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
299
313
|
|
|
300
314
|
```ts
|
|
315
|
+
import { addParent } from "@sv443-network/userutils";
|
|
316
|
+
|
|
301
317
|
// add an <a> around an element
|
|
302
318
|
const element = document.querySelector("#element");
|
|
303
319
|
const newParent = document.createElement("a");
|
|
304
320
|
newParent.href = "https://example.org/";
|
|
321
|
+
|
|
305
322
|
addParent(element, newParent);
|
|
306
323
|
```
|
|
307
324
|
|
|
@@ -321,6 +338,8 @@ Adds a global style to the page in form of a `<style>` element that's inserted i
|
|
|
321
338
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
322
339
|
|
|
323
340
|
```ts
|
|
341
|
+
import { addGlobalStyle } from "@sv443-network/userutils";
|
|
342
|
+
|
|
324
343
|
document.addEventListener("DOMContentLoaded", () => {
|
|
325
344
|
addGlobalStyle(`
|
|
326
345
|
body {
|
|
@@ -347,6 +366,8 @@ The resulting PromiseSettledResult array will contain the image elements if reso
|
|
|
347
366
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
348
367
|
|
|
349
368
|
```ts
|
|
369
|
+
import { preloadImages } from "@sv443-network/userutils";
|
|
370
|
+
|
|
350
371
|
preloadImages([
|
|
351
372
|
"https://example.org/image1.png",
|
|
352
373
|
"https://example.org/image2.png",
|
|
@@ -376,6 +397,8 @@ This function has to be run in response to a user interaction event, else the br
|
|
|
376
397
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
377
398
|
|
|
378
399
|
```ts
|
|
400
|
+
import { openInNewTab } from "@sv443-network/userutils";
|
|
401
|
+
|
|
379
402
|
document.querySelector("#my-button").addEventListener("click", () => {
|
|
380
403
|
openInNewTab("https://example.org/");
|
|
381
404
|
});
|
|
@@ -391,7 +414,7 @@ Usage:
|
|
|
391
414
|
interceptEvent(
|
|
392
415
|
eventObject: EventTarget,
|
|
393
416
|
eventName: string,
|
|
394
|
-
predicate: () => boolean
|
|
417
|
+
predicate: (event: Event) => boolean
|
|
395
418
|
): void
|
|
396
419
|
```
|
|
397
420
|
|
|
@@ -403,7 +426,10 @@ Calling this function will set the `Error.stackTraceLimit` to 1000 (if it's not
|
|
|
403
426
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
404
427
|
|
|
405
428
|
```ts
|
|
406
|
-
interceptEvent
|
|
429
|
+
import { interceptEvent } from "@sv443-network/userutils";
|
|
430
|
+
|
|
431
|
+
interceptEvent(document.body, "click", (event) => {
|
|
432
|
+
console.log("Intercepting click event:", event);
|
|
407
433
|
return true; // prevent all click events on the body element
|
|
408
434
|
});
|
|
409
435
|
```
|
|
@@ -417,7 +443,7 @@ Usage:
|
|
|
417
443
|
```ts
|
|
418
444
|
interceptWindowEvent(
|
|
419
445
|
eventName: string,
|
|
420
|
-
predicate: () => boolean
|
|
446
|
+
predicate: (event: Event) => boolean
|
|
421
447
|
): void
|
|
422
448
|
```
|
|
423
449
|
|
|
@@ -425,11 +451,14 @@ Intercepts all events dispatched on the `window` object and prevents the listene
|
|
|
425
451
|
This is essentially the same as [`interceptEvent()`](#interceptevent), but automatically uses the `unsafeWindow` (or falls back to regular `window`).
|
|
426
452
|
|
|
427
453
|
⚠️ This function should be called as soon as possible (I recommend using `@run-at document-start`), as it will only intercept events that are *attached* after this function is called.
|
|
454
|
+
⚠️ In order for all events to be interceptable, the directive `@grant unsafeWindow` should be set.
|
|
428
455
|
|
|
429
456
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
430
457
|
|
|
431
458
|
```ts
|
|
432
|
-
interceptWindowEvent
|
|
459
|
+
import { interceptWindowEvent } from "@sv443-network/userutils";
|
|
460
|
+
|
|
461
|
+
interceptWindowEvent("beforeunload", (event) => {
|
|
433
462
|
return true; // prevent the pesky "Are you sure you want to leave this page?" popup
|
|
434
463
|
});
|
|
435
464
|
```
|
|
@@ -463,6 +492,8 @@ The returned AmplifyMediaResult object has the following properties:
|
|
|
463
492
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
464
493
|
|
|
465
494
|
```ts
|
|
495
|
+
import { amplifyMedia } from "@sv443-network/userutils";
|
|
496
|
+
|
|
466
497
|
const audio = document.querySelector<HTMLAudioElement>("audio");
|
|
467
498
|
const button = document.querySelector<HTMLButtonElement>("button");
|
|
468
499
|
|
|
@@ -485,6 +516,31 @@ button.addEventListener("click", () => {
|
|
|
485
516
|
|
|
486
517
|
</details>
|
|
487
518
|
|
|
519
|
+
<br>
|
|
520
|
+
|
|
521
|
+
### isScrollable()
|
|
522
|
+
Usage:
|
|
523
|
+
```ts
|
|
524
|
+
isScrollable(element: Element): { horizontal: boolean, vertical: boolean }
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Checks if an element has a horizontal or vertical scroll bar.
|
|
528
|
+
This uses the computed style of the element, so it will also work if the element is hidden.
|
|
529
|
+
|
|
530
|
+
<details><summary><h4>Example - click to view</h4></summary>
|
|
531
|
+
|
|
532
|
+
```ts
|
|
533
|
+
import { isScrollable } from "@sv443-network/userutils";
|
|
534
|
+
|
|
535
|
+
const element = document.querySelector("#element");
|
|
536
|
+
const { horizontal, vertical } = isScrollable(element);
|
|
537
|
+
|
|
538
|
+
console.log("Element has a horizontal scroll bar:", horizontal);
|
|
539
|
+
console.log("Element has a vertical scroll bar:", vertical);
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
</details>
|
|
543
|
+
|
|
488
544
|
<br><br>
|
|
489
545
|
|
|
490
546
|
## Math:
|
|
@@ -500,6 +556,8 @@ Clamps a number between a min and max boundary (inclusive).
|
|
|
500
556
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
501
557
|
|
|
502
558
|
```ts
|
|
559
|
+
import { clamp } from "@sv443-network/userutils";
|
|
560
|
+
|
|
503
561
|
clamp(7, 0, 10); // 7
|
|
504
562
|
clamp(-1, 0, 10); // 0
|
|
505
563
|
clamp(5, -5, 0); // 0
|
|
@@ -531,6 +589,8 @@ Maps a number from one range to the spot it would be in another range.
|
|
|
531
589
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
532
590
|
|
|
533
591
|
```ts
|
|
592
|
+
import { mapRange } from "@sv443-network/userutils";
|
|
593
|
+
|
|
534
594
|
mapRange(5, 0, 10, 0, 100); // 50
|
|
535
595
|
mapRange(5, 0, 10, 0, 50); // 25
|
|
536
596
|
|
|
@@ -556,6 +616,8 @@ If only one argument is passed, it will be used as the `max` value and `min` wil
|
|
|
556
616
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
557
617
|
|
|
558
618
|
```ts
|
|
619
|
+
import { randRange } from "@sv443-network/userutils";
|
|
620
|
+
|
|
559
621
|
randRange(0, 10); // 4
|
|
560
622
|
randRange(10, 20); // 17
|
|
561
623
|
randRange(10); // 7
|
|
@@ -575,8 +637,9 @@ new ConfigManager(options: ConfigManagerOptions)
|
|
|
575
637
|
|
|
576
638
|
A class that manages a userscript's configuration that is persistently saved to and loaded from GM storage.
|
|
577
639
|
Also supports automatic migration of outdated data formats via provided migration functions.
|
|
640
|
+
You may create as many instances as you like as long as they have different IDs.
|
|
578
641
|
|
|
579
|
-
⚠️ The configuration is stored as a JSON string, so only JSON-compatible data can be used.
|
|
642
|
+
⚠️ The configuration is stored as a JSON string, so only JSON-compatible data can be used. Circular structures and complex objects will throw an error on load and save.
|
|
580
643
|
⚠️ The directives `@grant GM.getValue` and `@grant GM.setValue` are required for this to work.
|
|
581
644
|
|
|
582
645
|
The options object has the following properties:
|
|
@@ -608,7 +671,8 @@ Writes the default configuration given in `options.defaultConfig` synchronously
|
|
|
608
671
|
`deleteConfig(): Promise<void>`
|
|
609
672
|
Fully deletes the configuration from persistent storage.
|
|
610
673
|
The internal cache will be left untouched, so any subsequent calls to `getData()` will return the data that was last loaded.
|
|
611
|
-
If `loadData()` or `setData()` are called after this, the persistent storage will be populated again.
|
|
674
|
+
If `loadData()` or `setData()` are called after this, the persistent storage will be populated with the value of `options.defaultConfig` again.
|
|
675
|
+
⚠️ If you want to use this method, the additional directive `@grant GM.deleteValue` is required.
|
|
612
676
|
|
|
613
677
|
<br>
|
|
614
678
|
|
|
@@ -636,7 +700,7 @@ const formatVersion = 2;
|
|
|
636
700
|
/** Functions that migrate outdated data to the latest format - make sure a function exists for every previously used formatVersion and that no numbers are skipped! */
|
|
637
701
|
const migrations = {
|
|
638
702
|
// migrate from format version 0 to 1
|
|
639
|
-
1: (oldData:
|
|
703
|
+
1: (oldData: Record<string, unknown>) => {
|
|
640
704
|
return {
|
|
641
705
|
foo: oldData.foo,
|
|
642
706
|
bar: oldData.bar,
|
|
@@ -644,7 +708,7 @@ const migrations = {
|
|
|
644
708
|
};
|
|
645
709
|
},
|
|
646
710
|
// asynchronously migrate from format version 1 to 2
|
|
647
|
-
2: async (oldData:
|
|
711
|
+
2: async (oldData: Record<string, unknown>) => {
|
|
648
712
|
// arbitrary async operation required for the new format
|
|
649
713
|
const qux = JSON.parse(await (await fetch("https://api.example.org/some-data")).text());
|
|
650
714
|
return {
|
|
@@ -656,7 +720,7 @@ const migrations = {
|
|
|
656
720
|
},
|
|
657
721
|
};
|
|
658
722
|
|
|
659
|
-
const
|
|
723
|
+
const manager = new ConfigManager({
|
|
660
724
|
/** A unique ID for this configuration - choose wisely as changing it is not supported yet! */
|
|
661
725
|
id: "my-userscript",
|
|
662
726
|
/** Default / fallback configuration data */
|
|
@@ -672,7 +736,7 @@ async function init() {
|
|
|
672
736
|
// wait for the config to be loaded from persistent storage
|
|
673
737
|
// if no data was saved in persistent storage before or getData() is called before loadData(), the value of options.defaultConfig will be returned
|
|
674
738
|
// if the previously saved data needs to be migrated to a newer version, it will happen in this function call
|
|
675
|
-
const configData = await
|
|
739
|
+
const configData = await manager.loadData();
|
|
676
740
|
|
|
677
741
|
console.log(configData.foo); // "hello"
|
|
678
742
|
|
|
@@ -681,12 +745,12 @@ async function init() {
|
|
|
681
745
|
configData.bar = 123;
|
|
682
746
|
|
|
683
747
|
// save the updated config - synchronously to the cache and asynchronously to persistent storage
|
|
684
|
-
|
|
748
|
+
manager.saveData(configData).then(() => {
|
|
685
749
|
console.log("Config saved to persistent storage!");
|
|
686
750
|
});
|
|
687
751
|
|
|
688
752
|
// the internal cache is updated synchronously, so the updated data can be accessed before the Promise resolves:
|
|
689
|
-
console.log(
|
|
753
|
+
console.log(manager.getData().foo); // "world"
|
|
690
754
|
}
|
|
691
755
|
|
|
692
756
|
init();
|
|
@@ -708,6 +772,8 @@ If an array or NodeList is passed, the amount of contained items will be used.
|
|
|
708
772
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
709
773
|
|
|
710
774
|
```ts
|
|
775
|
+
import { autoPlural } from "@sv443-network/userutils";
|
|
776
|
+
|
|
711
777
|
autoPlural("apple", 0); // "apples"
|
|
712
778
|
autoPlural("apple", 1); // "apple"
|
|
713
779
|
autoPlural("apple", 2); // "apples"
|
|
@@ -734,6 +800,8 @@ Pauses async execution for a given amount of time.
|
|
|
734
800
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
735
801
|
|
|
736
802
|
```ts
|
|
803
|
+
import { pauseFor } from "@sv443-network/userutils";
|
|
804
|
+
|
|
737
805
|
async function run() {
|
|
738
806
|
console.log("Hello");
|
|
739
807
|
await pauseFor(3000); // waits for 3 seconds
|
|
@@ -759,6 +827,8 @@ The timeout will default to 300ms if left undefined.
|
|
|
759
827
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
760
828
|
|
|
761
829
|
```ts
|
|
830
|
+
import { debounce } from "@sv443-network/userutils";
|
|
831
|
+
|
|
762
832
|
window.addEventListener("resize", debounce((event) => {
|
|
763
833
|
console.log("Window was resized:", event);
|
|
764
834
|
}, 500)); // 500ms timeout
|
|
@@ -783,7 +853,9 @@ The timeout will default to 10 seconds if left undefined.
|
|
|
783
853
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
784
854
|
|
|
785
855
|
```ts
|
|
786
|
-
fetchAdvanced
|
|
856
|
+
import { fetchAdvanced } from "@sv443-network/userutils";
|
|
857
|
+
|
|
858
|
+
fetchAdvanced("https://jokeapi.dev/joke/Any?safe-mode", {
|
|
787
859
|
timeout: 5000,
|
|
788
860
|
// also accepts any other fetch options like headers:
|
|
789
861
|
headers: {
|
|
@@ -812,6 +884,8 @@ Returns undefined if the array is empty.
|
|
|
812
884
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
813
885
|
|
|
814
886
|
```ts
|
|
887
|
+
import { randomItem } from "@sv443-network/userutils";
|
|
888
|
+
|
|
815
889
|
randomItem(["foo", "bar", "baz"]); // "bar"
|
|
816
890
|
randomItem([ ]); // undefined
|
|
817
891
|
```
|
|
@@ -832,12 +906,15 @@ If the array is empty, it will return undefined for both values.
|
|
|
832
906
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
833
907
|
|
|
834
908
|
```ts
|
|
909
|
+
import { randomItemIndex } from "@sv443-network/userutils";
|
|
910
|
+
|
|
835
911
|
randomItemIndex(["foo", "bar", "baz"]); // ["bar", 1]
|
|
836
912
|
randomItemIndex([ ]); // [undefined, undefined]
|
|
913
|
+
|
|
837
914
|
// using array destructuring:
|
|
838
|
-
const [item, index] = randomItemIndex(["foo", "bar", "baz"]);
|
|
915
|
+
const [item, index] = randomItemIndex(["foo", "bar", "baz"]); // ["bar", 1]
|
|
839
916
|
// or if you only want the index:
|
|
840
|
-
const [, index] = randomItemIndex(["foo", "bar", "baz"]);
|
|
917
|
+
const [, index] = randomItemIndex(["foo", "bar", "baz"]); // 1
|
|
841
918
|
```
|
|
842
919
|
|
|
843
920
|
</details>
|
|
@@ -856,6 +933,8 @@ Returns undefined if the array is empty.
|
|
|
856
933
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
857
934
|
|
|
858
935
|
```ts
|
|
936
|
+
import { takeRandomItem } from "@sv443-network/userutils";
|
|
937
|
+
|
|
859
938
|
const arr = ["foo", "bar", "baz"];
|
|
860
939
|
takeRandomItem(arr); // "bar"
|
|
861
940
|
console.log(arr); // ["foo", "baz"]
|
|
@@ -877,7 +956,14 @@ If the array is empty, the originally passed empty array will be returned withou
|
|
|
877
956
|
<details><summary><h4>Example - click to view</h4></summary>
|
|
878
957
|
|
|
879
958
|
```ts
|
|
880
|
-
|
|
959
|
+
import { randomizeArray } from "@sv443-network/userutils";
|
|
960
|
+
|
|
961
|
+
const foo = [1, 2, 3, 4, 5, 6];
|
|
962
|
+
|
|
963
|
+
console.log(randomizeArray(foo)); // [3, 1, 5, 2, 4, 6]
|
|
964
|
+
console.log(randomizeArray(foo)); // [4, 5, 2, 1, 6, 3]
|
|
965
|
+
|
|
966
|
+
console.log(foo); // [1, 2, 3, 4, 5, 6] - original array is not mutated
|
|
881
967
|
```
|
|
882
968
|
|
|
883
969
|
</details>
|