@sv443-network/userutils 7.0.0 → 7.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @sv443-network/userutils
2
2
 
3
+ ## 7.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7335c59: Added Dialog class for easily creating customizable modal dialogs
8
+ - ca50132: Added NanoEmitter class for lightweight event emitter functionality in both OOP and FP
9
+
10
+ ## 7.0.1
11
+
12
+ ### Patch Changes
13
+
14
+ - d8f6852: Fix package.json exports
15
+
3
16
  ## 7.0.0
4
17
 
5
18
  ### Major Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- #MARKER Description -->
4
4
  ## UserUtils
5
- Zero-dependency library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and more.
5
+ Lightweight library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and more.
6
6
 
7
7
  Contains builtin TypeScript declarations. Fully web compatible and supports ESM and CJS imports and global declaration.
8
8
  If you like using this library, please consider [supporting the development ❤️](https://github.com/sponsors/Sv443)
@@ -13,7 +13,7 @@ View the documentation of previous major releases:
13
13
  </sup>
14
14
  <sub>
15
15
 
16
- <a href="https://github.com/Sv443-Network/UserUtils/blob/v5.0.1/README.md" rel="noopener noreferrer">5.0.1</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v4.2.1/README.md" rel="noopener noreferrer">4.2.1</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v3.0.0/README.md" rel="noopener noreferrer">3.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v2.0.1/README.md" rel="noopener noreferrer">2.0.1</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v1.2.0/README.md" rel="noopener noreferrer">1.2.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v0.5.3/README.md" rel="noopener noreferrer">0.5.3</a>
16
+ <a href="https://github.com/Sv443-Network/UserUtils/blob/v6.3.0/README.md" rel="noopener noreferrer">6.3.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v5.0.1/README.md" rel="noopener noreferrer">5.0.1</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v4.2.1/README.md" rel="noopener noreferrer">4.2.1</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v3.0.0/README.md" rel="noopener noreferrer">3.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v2.0.1/README.md" rel="noopener noreferrer">2.0.1</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v1.2.0/README.md" rel="noopener noreferrer">1.2.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v0.5.3/README.md" rel="noopener noreferrer">0.5.3</a>
17
17
 
18
18
  </sub>
19
19
  </div>
@@ -44,6 +44,8 @@ View the documentation of previous major releases:
44
44
  - [**Misc:**](#misc)
45
45
  - [`DataStore`](#datastore) - class that manages a hybrid sync & async persistent JSON database, including data migration
46
46
  - [`DataStoreSerializer`](#datastoreserializer) - class for importing & exporting data of multiple DataStore instances, including compression, checksumming and running migrations
47
+ - [`Dialog`](#dialog) - class for creating custom modal dialogs with a promise-based API and a generic, default style
48
+ - [`NanoEmitter`](#nanoemitter) - tiny event emitter class with a focus on performance and simplicity (based on [nanoevents](https://npmjs.com/package/nanoevents))
47
49
  - [`autoPlural()`](#autoplural) - automatically pluralize a string
48
50
  - [`pauseFor()`](#pausefor) - pause the execution of a function for a given amount of time
49
51
  - [`debounce()`](#debounce) - call a function only once in a series of calls, after or before a given timeout
@@ -60,9 +62,9 @@ View the documentation of previous major releases:
60
62
  - [`randomizeArray()`](#randomizearray) - returns a copy of the array with its items in a random order
61
63
  - [**Translation:**](#translation)
62
64
  - [`tr()`](#tr) - simple translation of a string to another language
63
- - [tr.addLanguage()](#traddlanguage) - add a language and its translations
64
- - [tr.setLanguage()](#trsetlanguage) - set the currently active language for translations
65
- - [tr.getLanguage()](#trgetlanguage) - returns the currently active language
65
+ - [`tr.addLanguage()`](#traddlanguage) - add a language and its translations
66
+ - [`tr.setLanguage()`](#trsetlanguage) - set the currently active language for translations
67
+ - [`tr.getLanguage()`](#trgetlanguage) - returns the currently active language
66
68
  - [**Utility types for TypeScript:**](#utility-types)
67
69
  - [`Stringifiable`](#stringifiable) - any value that is a string or can be converted to one (implicitly or explicitly)
68
70
  - [`NonEmptyArray`](#nonemptyarray) - any array that should have at least one item
@@ -1246,7 +1248,240 @@ async function importMyDataPls() {
1246
1248
  ```
1247
1249
  </details>
1248
1250
 
1249
- <br><br>
1251
+ <br>
1252
+
1253
+ ### Dialog
1254
+ Usage:
1255
+ ```ts
1256
+ new Dialog(options: DialogOptions)
1257
+ ```
1258
+
1259
+ A class that creates a customizable modal dialog with a title (optional), body and footer (optional).
1260
+ There are tons of options for customization, like changing the close behavior, translating strings and more.
1261
+
1262
+ The options object has the following properties:
1263
+ | Property | Description |
1264
+ | :-- | :-- |
1265
+ | `id: string` | A unique internal identification string for this instance. If two Dialogs share the same ID, they will overwrite each other. |
1266
+ | `width: number` | The target and maximum width of the dialog in pixels. |
1267
+ | `height: number` | The target and maximum height of the dialog in pixels. |
1268
+ | `renderBody: () => HTMLElement \| Promise<HTMLElement>` | Called to render the body of the dialog. |
1269
+ | `renderHeader?: () => HTMLElement \| Promise<HTMLElement>` | (Optional) Called to render the header of the dialog. Leave undefined for a blank header. |
1270
+ | `renderFooter?: () => HTMLElement \| Promise<HTMLElement>` | (Optional) Called to render the footer of the dialog. Leave undefined for no footer. |
1271
+ | `closeOnBgClick?: boolean` | (Optional) Whether the dialog should close when the background is clicked. Defaults to `true`. |
1272
+ | `closeOnEscPress?: boolean` | (Optional) Whether the dialog should close when the escape key is pressed. Defaults to `true`. |
1273
+ | `destroyOnClose?: boolean` | (Optional) Whether the dialog should be destroyed when it's closed. Defaults to `false`. |
1274
+ | `unmountOnClose?: boolean` | (Optional) Whether the dialog should be unmounted when it's closed. Defaults to `true`. Superseded by `destroyOnClose`. |
1275
+ | `removeListenersOnDestroy?: boolean` | (Optional) Whether all listeners should be removed when the dialog is destroyed. Defaults to `true`. |
1276
+ | `small?: boolean` | (Optional) Whether the dialog should have a smaller overall appearance. Defaults to `false`. |
1277
+ | `verticalAlign?: "top" \| "center" \| "bottom"` | (Optional) Where to align or anchor the dialog vertically. Defaults to `"center"`. |
1278
+ | `strings?: Partial<typeof defaultStrings>` | (Optional) Strings used in the dialog (used for translations). Defaults to the default English strings (importable with the name `defaultStrings`). |
1279
+ | `dialogCss?: string` | (Optional) CSS to apply to the dialog. Defaults to the default (importable with the name `defaultDialogCss`). |
1280
+
1281
+ Methods:
1282
+ `open(): Promise<void>`
1283
+ Opens the dialog.
1284
+
1285
+ `close(): void`
1286
+ Closes the dialog.
1287
+
1288
+ `mount(): Promise<void>`
1289
+ Mounts the dialog to the DOM by calling the render functions provided in the options object.
1290
+ Can be done before opening the dialog to avoid a delay.
1291
+
1292
+ `unmount(): void`
1293
+ Unmounts the dialog from the DOM.
1294
+
1295
+ `remount(): Promise<void>`
1296
+ Unmounts and mounts the dialog again.
1297
+ The render functions in the options object will be called again.
1298
+ May cause a flickering effect due to the rendering delay.
1299
+
1300
+ `isOpen(): boolean`
1301
+ Returns `true` if the dialog is open, else `false`.
1302
+
1303
+ `isMounted(): boolean`
1304
+ Returns `true` if the dialog is mounted, else `false`.
1305
+
1306
+ `destroy(): void`
1307
+ Destroys the dialog.
1308
+ Removes all listeners and unmounts the dialog by default.
1309
+
1310
+ `static getCurrentDialogId(): string`
1311
+ Static method that returns the ID of the currently open dialog.
1312
+ Needs to be called without creating an instance of the class.
1313
+
1314
+ `static getOpenDialogs(): string[]`
1315
+ Static method that returns an array of the IDs of all open dialogs.
1316
+ Needs to be called without creating an instance of the class.
1317
+
1318
+ <details><summary><b>Example - click to view</b></summary>
1319
+
1320
+ ```ts
1321
+ import { Dialog } from "@sv443-network/userutils";
1322
+
1323
+ const fooDialog = new Dialog({
1324
+ id: "foo-dialog",
1325
+ width: 400,
1326
+ height: 300,
1327
+ renderHeader() {
1328
+ const header = document.createElement("div");
1329
+ header.textContent = "This is the header";
1330
+ return header;
1331
+ },
1332
+ renderBody() {
1333
+ const body = document.createElement("div");
1334
+ body.textContent = "This is the body";
1335
+ return body;
1336
+ },
1337
+ renderFooter() {
1338
+ const footer = document.createElement("div");
1339
+ footer.textContent = "This is the footer";
1340
+ return footer;
1341
+ },
1342
+ closeOnBgClick: true,
1343
+ closeOnEscPress: true,
1344
+ destroyOnClose: false,
1345
+ unmountOnClose: true,
1346
+ removeListenersOnDestroy: true,
1347
+ small: false,
1348
+ verticalAlign: "center",
1349
+ strings: {
1350
+ closeDialogTooltip: "Click to close",
1351
+ },
1352
+ dialogCss: getMyCustomDialogCss(),
1353
+ });
1354
+
1355
+ fooDialog.on("close", () => {
1356
+ console.log("Dialog closed");
1357
+ });
1358
+
1359
+ fooDialog.open();
1360
+ ```
1361
+
1362
+ </details>
1363
+
1364
+ <br>
1365
+
1366
+ ### NanoEmitter
1367
+ Usage:
1368
+ ```ts
1369
+ new NanoEmitter<TEventMap = EventsMap>(options?: NanoEmitterOptions): NanoEmitter<TEventMap>
1370
+ ```
1371
+
1372
+ A class that provides a minimalistic event emitter with a tiny footprint powered by [nanoevents.](https://npmjs.com/package/nanoevents)
1373
+ The `TEventMap` generic is used to define the events that can be emitted and listened to.
1374
+
1375
+ The intention behind this class is to extend it in your own classes to provide a simple event system.
1376
+ You can also just create an instance and export it to use it as standalone event emitters throughout your project.
1377
+
1378
+ The options object has the following properties:
1379
+ | Property | Description |
1380
+ | :-- | :-- |
1381
+ | `publicEmit?: boolean` | (Optional) If set to true, allows emitting events through the public method `emit()` (`false` by default). |
1382
+
1383
+ Methods:
1384
+ `on<K extends keyof TEventMap>(event: K, listener: TEventMap[K]): void`
1385
+ Registers a listener function for the given event.
1386
+ May be called multiple times for the same event.
1387
+
1388
+ `once<K extends keyof TEventMap>(event: K, listener: TEventMap[K]): void`
1389
+ Registers a listener function for the given event that will only be called once.
1390
+
1391
+ `emit<K extends keyof TEventMap>(event: K, ...args: Parameters<TEventMap[K]>): boolean`
1392
+ Emits an event with the given arguments from outside the class instance if `publicEmit` is set to `true`.
1393
+ If `publicEmit` is set to `true`, this method is public and can be called from outside the class and will return `true`.
1394
+ If not, it is protected and will return `false`.
1395
+
1396
+ `unsubscribeAll(): void`
1397
+ Removes all listeners from all events.
1398
+
1399
+ <br>
1400
+
1401
+ <details><summary><b>Object oriented example - click to view</b></summary>
1402
+
1403
+ ```ts
1404
+ import { NanoEmitter } from "@sv443-network/userutils";
1405
+
1406
+ interface MyEventMap {
1407
+ foo: (bar: string) => void;
1408
+ baz: (qux: number) => void;
1409
+ }
1410
+
1411
+ class MyClass extends NanoEmitter<MyEventMap> {
1412
+ constructor() {
1413
+ super({
1414
+ // allow emitting events from outside the class
1415
+ publicEmit: true,
1416
+ });
1417
+ this.once("baz", (qux) => {
1418
+ console.log("baz event (inside):", qux);
1419
+ });
1420
+ }
1421
+
1422
+ public doStuff() {
1423
+ this.emit("foo", "hello");
1424
+ this.emit("baz", 42);
1425
+ this.emit("foo", "world");
1426
+ this.emit("baz", 69);
1427
+ }
1428
+ }
1429
+
1430
+ const myInstance = new MyClass();
1431
+ myInstance.doStuff();
1432
+
1433
+ myInstance.on("foo", (bar) => {
1434
+ console.log("foo event (outside):", bar);
1435
+ });
1436
+
1437
+ myInstance.emit("baz", "hello from the outside");
1438
+
1439
+ myInstance.unsubscribeAll();
1440
+ ```
1441
+
1442
+ </details>
1443
+
1444
+ <br>
1445
+
1446
+ <details><summary><b>Functional example - click to view</b></summary>
1447
+
1448
+ ```ts
1449
+ import { NanoEmitter } from "@sv443-network/userutils";
1450
+
1451
+ interface MyEventMap {
1452
+ foo: (bar: string) => void;
1453
+ baz: (qux: number) => void;
1454
+ }
1455
+
1456
+ const myEmitter = new NanoEmitter<MyEventMap>({
1457
+ // allow emitting events from outside the class
1458
+ publicEmit: true,
1459
+ });
1460
+
1461
+ myEmitter.on("foo", (bar) => {
1462
+ console.log("foo event:", bar);
1463
+ });
1464
+
1465
+ myEmitter.once("baz", (qux) => {
1466
+ console.log("baz event:", qux);
1467
+ });
1468
+
1469
+ function doStuff() {
1470
+ myEmitter.emit("foo", "hello");
1471
+ myEmitter.emit("baz", 42);
1472
+ myEmitter.emit("foo", "world");
1473
+ myEmitter.emit("baz", 69);
1474
+
1475
+ myEmitter.emit("foo", "hello from the outside");
1476
+
1477
+ myEmitter.unsubscribeAll();
1478
+ }
1479
+
1480
+ doStuff();
1481
+ ```
1482
+ </details>
1483
+
1484
+ <br>
1250
1485
 
1251
1486
  ### autoPlural()
1252
1487
  Usage: