UNPKG

@alessiofrittoli/web-utils

Version:
2,105 lines (1,304 loc) • 80.7 kB
<h1 align="center">Web Utils 🛠️</h1> <p align="center"> Common TypeScript web utilities </p> <p align="center"> <a href="https://npmjs.org/package/@alessiofrittoli/web-utils"> <img src="https://img.shields.io/npm/v/@alessiofrittoli/web-utils" alt="Latest version"/> </a> <a href="https://coveralls.io/github/alessiofrittoli/web-utils"> <img src="https://coveralls.io/repos/github/alessiofrittoli/web-utils/badge.svg" alt="Test coverage"/> </a> <a href="https://socket.dev/npm/package/@alessiofrittoli/web-utils/overview"> <img src="https://socket.dev/api/badge/npm/package/@alessiofrittoli/web-utils" alt="Socket Security score"/> </a> <a href="https://npmjs.org/package/@alessiofrittoli/web-utils"> <img src="https://img.shields.io/npm/dm/@alessiofrittoli/web-utils.svg" alt="npm downloads"/> </a> <a href="https://bundlephobia.com/package/@alessiofrittoli/web-utils"> <img src="https://badgen.net/bundlephobia/dependency-count/@alessiofrittoli/web-utils" alt="Dependencies"/> </a> <a href="https://libraries.io/npm/%40alessiofrittoli%2Fweb-utils"> <img src="https://img.shields.io/librariesio/release/npm/@alessiofrittoli/web-utils" alt="Dependencies status"/> </a> </p> <p align="center"> <a href="https://bundlephobia.com/package/@alessiofrittoli/web-utils"> <img src="https://badgen.net/bundlephobia/min/@alessiofrittoli/web-utils" alt="minified"/> </a> <a href="https://bundlephobia.com/package/@alessiofrittoli/web-utils"> <img src="https://badgen.net/bundlephobia/minzip/@alessiofrittoli/web-utils" alt="minizipped"/> </a> <a href="https://bundlephobia.com/package/@alessiofrittoli/web-utils"> <img src="https://badgen.net/bundlephobia/tree-shaking/@alessiofrittoli/web-utils" alt="Tree shakable"/> </a> </p> <p align="center"> <a href="https://github.com/sponsors/alessiofrittoli"> <img src="https://img.shields.io/static/v1?label=Fund%20this%20package&message=%E2%9D%A4&logo=GitHub&color=%23DB61A2" alt="Fund this package"/> </a> </p> [sponsor-badge]: https://img.shields.io/static/v1?label=Fund%20this%20package&message=%E2%9D%A4&logo=GitHub&color=%23DB61A2 [sponsor-url]: https://github.com/sponsors/alessiofrittoli ### Table of Contents - [Getting started](#getting-started) - [What's Changed](#whats-changed) - [API Reference](#api-reference) - [Blob utilities](#blob-utilities) - [Array utilities](#array-utilities) - [Dom utilities](#dom-utilities) - [Scroll utilities](#scroll-utilities) - [Stylesheet utilities](#stylesheet-utilities) - [Generators utilities](#generators-utilities) - [Map utilities](#map-utilities) - [Promises utilities](#promises-utilities) - [Strings utilities](#strings-utilities) - [Types utilities](#types-utilities) - [Validation utilities](#validation-utilities) - [Objects utilities](#objects-utilities) - [Browser API utilities](#browser-api-utilities) - [Document Picture-in-Picture](#document-picture-in-picture) - [Network Information](#network-information) - [Device utilities](#device-utilities) - [Storage utilities](#storage-utilities) - [`Cookie` Class](#cookie-class) - [`LocalStorage` Class](#localstorage-class) - [`SessionStorage` Class](#sessionstorage-class) - [Utils](#utils) - [Development](#development) - [Install depenendencies](#install-depenendencies) - [Build the source code](#build-the-source-code) - [ESLint](#eslint) - [Jest](#jest) - [Contributing](#contributing) - [Security](#security) - [Credits](#made-with-) --- ### Getting started Run the following command to start using `web-utils` in your projects: ```bash npm i @alessiofrittoli/web-utils ``` or using `pnpm` ```bash pnpm i @alessiofrittoli/web-utils ``` --- ### What's Changed #### Updates in the latest release 🎉 - Add `normalizeIndex` utility function. See [API Reference](#normalizeindex) for more info. - Add `getPreviousIndex` utility function. See [API Reference](#getpreviousindex) for more info. - Add `getNextIndex` utility function. See [API Reference](#getnextindex) for more info. - Add `insertAfter` utility function. See [API Reference](#insertafter) for more info. Old updates: - Add `deferTask`. See [API Reference](#defertask) for more info. - Add `deferCallback`. See [API Reference](#defercallback) for more info. - Add `parameterized` function. See [API Reference](#parameterized) for more info. - Add Document Picture-in-Picture utilities. See [Document Picture-in-Picture](#document-picture-in-picture) for more info. --- ### API Reference #### Array utilities ##### `arrayUnique` Removes duplicate values from an array. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | ----- | ---------------- | | `array` | `T[]` | The input array. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `T[]` The filtered array. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Removes duplicates from array ```ts import { arrayUnique } from "@alessiofrittoli/web-utils"; const pointer = {}; console.log(arrayUnique([pointer, "b", pointer, "c", "b"])); // Outputs: [ {}, 'b', 'c' ] ``` </details> --- ##### `arrayObjectUnique` Removes duplicate entries from an array referencing an object key. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ---------- | --------- | -------------------------------- | | `array` | `T[]` | An array of objects. | | `property` | `keyof T` | The Object property to refer to. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `T[]` The filtered array. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Removes duplicates from array with the same propery value ```ts import { arrayObjectUnique } from "@alessiofrittoli/web-utils"; const arr = [ { id: 1, name: "a" }, { id: 2, name: "b" }, { id: 1, name: "c" }, // duplicate `id` { id: 3, name: "d" }, { id: 4, name: "a" }, // duplicate `name` ]; console.log(arrayObjectUnique(arr, "id")); // Outputs: [ // { id: 1, name: 'a' }, // { id: 2, name: 'b' }, // { id: 3, name: 'd' }, // { id: 4, name: 'a' }, // ] console.log(arrayObjectUnique(arr, "name")); // Outputs: [ // { id: 1, name: 'a' }, // { id: 2, name: 'b' }, // { id: 1, name: 'c' }, // { id: 3, name: 'd' }, // ] ``` </details> --- ##### `listToArray` Convert a stringified Array to Array object. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | -------------------- | | `string` | `string` | An array of objects. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `string[]` The converted stringified Array to Array object. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Basic usage ```ts import { listToArray } from "@alessiofrittoli/web-utils"; console.log(listToArray("1,2, 3, 4").map(Number)); // Outputs: [ 1, 2, 3, 4 ] ``` </details> --- ##### `chunkInto` Split Array into chunks. <details> <summary style="cursor:pointer">Type Parameters</summary> | Parameter | Default | Description | | --------- | ----------- | ----------------------- | | `T` | `unknown[]` | The input `array` type. | </details> --- <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------- | | `array` | `T[]` | The original Array. | | `options` | `ChunkIntoOptions` | An object defining split criteria. | | `options.size` | `number` | Will split the given Array in a way to ensure each chunk length is, whenever possible, equal to the given value. | | `options.count` | `number` | Will split the given Array in a way to ensure n chunks as the given value. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `T[]` An Array of chunks. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Basic usage ```ts import { chunkInto } from "@alessiofrittoli/web-utils"; console.log(chunkInto([1, 2, 3, 4, 5], { count: 2 })); // Output: [ [ 1, 2, 3 ], [ 4, 5 ] ] console.log(chunkInto([1, 2, 3, 4, 5], { size: 2 })); // Output: [ [ 1, 2 ], [ 3, 4 ], [ 5 ] ] ``` </details> --- ##### `shuffle` Shuffle the elements of an array in place using the [Fisher-Yates algorithm](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle). Please note that this function modify the original given `array`. <details> <summary style="cursor:pointer">Type parameters</summary> | Parameter | Description | | --------- | --------------------------------------------------------- | | `T` | The automatically inferred type of elements in the array. | </details> --- <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | ---------- | --------------------- | | `array` | `Array<T>` | The array to shuffle. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `Array<T>` The modified shuffled array. </details> --- <details> <summary style="cursor:pointer">Example</summary> ```ts import { shuffle } from "@alessiofrittoli/web-utils"; console.log(shuffle([1, 2, 3, 4, 5])); ``` </details> --- ##### `shuffleCopy` Copy and shuffle the elements of an array in place using the [Fisher-Yates algorithm](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle). Same API of [`shuffle`](#shuffle) is applied, but this function **does not modify** the original given `array`. --- ##### `normalizeIndex` Normalize negative indexes to a positive value. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | ----------------- | | `index` | `number` | The index. | | `length` | `number` | The array length. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `number` The normalized index. </details> --- <details> <summary style="cursor:pointer">Examples</summary> ```ts import { normalizeIndex } from "@alessiofrittoli/web-utils"; const array = [0, 1, 2, 3]; console.log(normalizeIndex(2, array.length)); // Outputs: 2 console.log(normalizeIndex(-1, array.length)); // Outputs: 3 console.log(normalizeIndex(-3, array.length)); // Outputs: 1 ``` </details> --- ##### `getPreviousIndex` Get the previous index. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Default | Description | | --------- | -------- | ------- | ----------------- | | `length` | `number` | - | The array length. | | `index` | `number` | `0` | The index. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `number` The previous index. </details> --- <details> <summary style="cursor:pointer">Examples</summary> ```ts import { getPreviousIndex } from "@alessiofrittoli/web-utils"; const array = [0, 1, 2, 3]; console.log(getPreviousIndex(array.length, 2)); // Outputs: 1 console.log(getPreviousIndex(array.length, -1)); // Outputs: 2 console.log(getPreviousIndex(array.length, -3)); // Outputs: 0 ``` </details> --- ##### `getNextIndex` Get the next index. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Default | Description | | --------- | -------- | ------- | ----------------- | | `length` | `number` | - | The array length. | | `index` | `number` | `0` | The index. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `number` The next index. </details> --- <details> <summary style="cursor:pointer">Examples</summary> ```ts import { getNextIndex } from "@alessiofrittoli/web-utils"; const array = [0, 1, 2, 3]; console.log(getNextIndex(array.length, 2)); // Outputs: 3 console.log(getNextIndex(array.length, -1)); // Outputs: 0 console.log(getNextIndex(array.length, -3)); // Outputs: 2 ``` </details> --- ##### `insertAfter` Inserts one or more items into an array at the specified index position. <details> <summary style="cursor:pointer">Type Parameters</summary> - `T` The type of elements in the array. </details> --- <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Default | Description | | --------- | -------- | ------- | --------------------------------------------- | | `array` | `T[]` | - | The original array to insert items into. | | `item` | `T\|T[]` | 0 | A single item or an array of items to insert. | | `index` | `number` | `-1` | The index after which to insert the item(s). | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `T[]` A new array with the item(s) inserted at the specified position. </details> --- <details> <summary style="cursor:pointer">Examples</summary> ```ts import { insertAfter } from "@alessiofrittoli/web-utils"; console.log(insertAfter([1, 2, 3, 4], 5)); // Outputs: [ 1, 2, 3, 4, 5 ] console.log(insertAfter(["a", "c"], "b", 0)); // Outputs: [ 'a', 'b', 'c' ] console.log(insertAfter(["a", "b", "c", "e"], "d", -2)); // Outputs: [ 'a', 'b', 'c', 'd', 'e' ] ``` </details> --- ##### `findIndexBy` Finds the index of the first object whose selected field strictly equals the provided value. <details> <summary style="cursor:pointer">Type Parameters</summary> - `T` The object type stored in the array. - `U` The key of `T` used for the comparison. </details> --- <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------------- | --------------------------- | ------------------------------------------------- | | `options` | `FindInxeByOptions<T, U>` | The search options. | | `options.items` | `T[]` | The collection to search. | | `options.field` | `U` | The object field to compare against `value`. | | `options.value` | `T[U]` | The value that must match the selected field. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `number` The index of the matching item, or `-1` when no match is found. </details> --- <details> <summary style="cursor:pointer">Examples</summary> ```ts import { findIndexBy } from "@alessiofrittoli/web-utils"; const users = [ { id: 1, name: "Ada" }, { id: 2, name: "Grace" }, { id: 3, name: "Linus" }, ]; console.log(findIndexBy({ items: users, field: "id", value: 2 })); // Outputs: 1 console.log(findIndexBy({ items: users, field: "name", value: "Linus" })); // Outputs: 2 console.log(findIndexBy({ items: users, field: "id", value: 9 })); // Outputs: -1 ``` </details> --- #### Blob utilities ##### `downloadBlob` Create and download a blob object. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ---------- | -------------- | ----------------------------------- | | `filename` | `string` | The download file name. | | `data` | `BodyInit` | The download file data. | | `init` | `ResponseInit` | (Optional) The ResponseInit object. | </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Download file from HTTP Response ```ts import { downloadBlob } from '@alessiofrittoli/web-utils' fetch( ... ) .then( response => response.formData() ) .then( async data => { await Promise.all( Array.from( data.entries() ) .map( async ( [, file ] ) => { if ( ! ( file instanceof File ) ) return await downloadBlob( file.name, file ) } ) ) } ) .catch( error => { console.error( error ) } ) ``` </details> --- #### Dom utilities ##### Scroll utilities ###### `blockScroll` Prevent Element Overflow. It calculates the scrollbar width and the resulting value is applied to the target element right padding-right to prevent width grows. It also applies the `--scrollbar-size` CSS variable that can be used to apply a padding-right to the position fixed elements inside the target. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Default | Description | | --------- | ------------- | -------------------------- | ------------------------------ | | `target` | `HTMLElement` | `Document.documentElement` | (Optional) The target Element. | </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Block Document HTML scroll when a popup is opened ```ts import { blockScroll } from "@alessiofrittoli/web-utils"; const openPopUpHandler = () => { blockScroll(); // ... handle popup }; ``` ```css .modal-wrapper { position: fixed; inset: 0; padding-right: var(--scrollbar-size, 0); } ``` --- ###### Block scroll of a specific HTMLElement ```ts import { blockScroll } from "@alessiofrittoli/web-utils"; const element = document.querySelector(".css-selector"); if (element) { blockScroll(element); } ``` </details> --- ###### `restoreScroll` Restore Element Overflow. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Default | Description | | --------- | ------------- | -------------------------- | ------------------------------ | | `target` | `HTMLElement` | `Document.documentElement` | (Optional) The target Element. | </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Restore Document HTML scroll when a popup is closed ```ts import { restoreScroll } from "@alessiofrittoli/web-utils"; const closePopUpHandler = () => { // ... handle close restoreScroll(); }; ``` --- ###### Restore scroll of a specific HTMLElement ```ts import { restoreScroll } from "@alessiofrittoli/web-utils"; const element = document.querySelector(".css-selector"); if (element) { restoreScroll(element); } ``` </details> --- ##### Stylesheet utilities ###### Types ###### UrlStylesheet Represents a URL stylesheet as a simple URL input, URL object or as an object with URL and fetch configuration options. ```ts type UrlStylesheet = | UrlInput | { /** * The URL string or a URL object of the stylesheet to load. * */ url: UrlInput; /** * Indicates whether to fetch the given URL. * * @default false */ fetch?: boolean; }; ``` --- ###### Style Represents a style input. ```ts type Style = UrlStylesheet | HTMLStyleElement | CSSStyleSheet | StyleSheetList; ``` --- ###### Styles Represents a single style object or an array of style objects. ```ts type Styles = Style | Style[]; ``` --- ###### `cloneStyleSheetList` Clones a StyleSheetList or array of CSSStyleSheets into an array of `HTMLStyleElement` objects. This function extracts CSS rules from each stylesheet and creates corresponding `<style>` elements containing the serialized CSS text. If an error occurs while processing a stylesheet, the error is logged and that stylesheet is skipped. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | --------------------------------- | ------------------------------------------------------------------------- | | `styles` | `StyleSheetList\|CSSStyleSheet[]` | The source `StyleSheetList` or array of `CSSStyleSheet` objects to clone. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `HTMLStyleElement[]` - An array of `HTMLStyleElement` objects, each containing the CSS rules from the source stylesheets. - Failed stylesheets are filtered out and not included in the result. </details> --- <details> <summary style="cursor:pointer">Examples</summary> ```ts import { cloneStyleSheetList } from "@alessiofrittoli/web-utils"; const styles = cloneStyleSheetList(document.styleSheets); // do something with cloned stylesheets // styles.forEach( style => shadowRoot.appendChild( style ) ) ``` </details> --- ###### `cloneStyleSheets` Clones style sheets from various sources into new `HTMLStyleElement` nodes. - When a URL stylesheet has `fetch: true`, the stylesheet content is fetched and embedded as inline CSS. - When `fetch: false` (default), a link element is created instead. - URL parsing is handled through the `Url` utility with support for both string and `UrlInput` object formats. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | ------------------------------------------------------------- | | `styles` | `Styles` | A style source or array of style sources. | | | | - See [`Styles`](#styles) type for a list of possible values. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `Promise<CloneStyleSheetsReturn>` - A promise that resolves to an array of cloned `HTMLStyleElement` and `HTMLLinkElement` nodes. - For inline styles and StyleSheetLists, returns `HTMLStyleElement` nodes. - For URL-based stylesheets, returns `HTMLLinkElement` nodes (or `HTMLStyleElement` if fetch is `true`). - Failed operations are silently ignored. </details> --- <details> <summary style="cursor:pointer">Examples</summary> ###### Cloning `StyleSheetList` ```ts import { cloneStyleSheets } from "@alessiofrittoli/web-utils"; const styles = await cloneStyleSheets(document.styleSheets); // do something with cloned dcoument stylesheets // styles.forEach( style => shadowRoot.appendChild( style ) ) ``` --- ###### Cloning stylesheets from URL ```ts import { cloneStyleSheets } from "@alessiofrittoli/web-utils"; const styles = await cloneStyleSheets("/path-to-stylesheet-file.css"); const styles = await cloneStyleSheets({ url: "/path-to-stylesheet-file-2.css", fetch: true, }); const styles = await cloneStyleSheets([ "/path-to-stylesheet-file-3.css", { url: "/path-to-stylesheet-file-4.css", fetch: true }, ]); ``` </details> --- #### Generators utilities ##### `isGeneratorFunction` Check if a function is a `GeneratorFunction` or `AsyncGeneratorFunction`. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ----------- | --------- | ---------------------- | | `reference` | `unknown` | The function to check. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `reference` is `GeneratorFunction | AsyncGeneratorFunction` - `true` if the given `reference` is a `GeneratorFunction` or `AsyncGeneratorFunction`. - `false` otherwise. </details> --- ##### `isDefaultGeneratorFunction` Check if a function is a `GeneratorFunction`. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ----------- | --------- | ---------------------- | | `reference` | `unknown` | The function to check. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `reference` is `GeneratorFunction` - `true` if the given `reference` is a `GeneratorFunction`. - `false` otherwise. </details> --- ##### `isAsyncGeneratorFunction` Check if a function is an `AsyncGeneratorFunction`. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ----------- | --------- | ---------------------- | | `reference` | `unknown` | The function to check. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `reference` is `AsyncGeneratorFunction` - `true` if the given `reference` is an `AsyncGeneratorFunction`. - `false` otherwise. </details> --- ##### `isGeneratorObject<T>` Check if reference is a `Generator` or `AsyncGenerator`. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ----------- | --------- | ----------------------- | | `reference` | `unknown` | The reference to check. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `reference` is `Generator<T> | AsyncGenerator<T>` - `true` if the given `reference` is a `Generator` or `AsyncGenerator`. - `false` otherwise. </details> --- ##### `isDefaultGeneratorObject<T>` Check if reference is a `Generator`. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ----------- | --------- | ----------------------- | | `reference` | `unknown` | The reference to check. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `reference` is `Generator<T>` - `true` if the given `reference` is a `Generator`. - `false` otherwise. </details> --- ##### `isAsyncGeneratorObject<T>` Check if reference is an `AsyncGenerator`. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ----------- | --------- | ----------------------- | | `reference` | `unknown` | The reference to check. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `reference` is `AsyncGenerator<T>` - `true` if the given `reference` is an `AsyncGenerator`. - `false` otherwise. </details> --- #### Map utilities ##### Interface `TypedMap<T, P, K>` A type-safe extension of the Map class that enforces key-value relationships based on a provided type. <details> <summary style="cursor:pointer">Type parameters</summary> | Parameter | Type | Default | Description | | --------- | ------------------------- | --------- | ----------------------------------------------------------------------------------- | | `T` | `Record<string, unknown>` | `unknown` | The object type defining the key-value relationships. | | `P` | `boolean` | `true` | Defines whether the `Map.get()` method should return a possibily `undefined` value. | | `K` | `keyof T` | `keyof T` | Internal - The subset of keys in T that are allowed in the Map. | </details> --- ##### `getTypedMap<T, P, K>` Creates a new instance of a type-safe `Map` with the given type. <details> <summary style="cursor:pointer">Type parameters</summary> - See [Interface `TypedMap<T, P, K>` - Type parameters](#interface-typedmapt-p-k) </details> --- <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | ---------- | ------------------------------------------ | ------------------------------------------ | | `iterable` | `Iterable<readonly [ K, T[ K ] ]> \| null` | Initial `Map` constructor iterable object. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `TypedMap<T, P, K>` A new instance of a type-safe `Map`. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Basic usage ```ts import { getTypedMap } from "@alessiofrittoli/web-utils"; interface User { name: string; age: number; isActive: boolean; } const user = getTypedMap<User>([ ["name", "Foo"], ["age", 27], ["isActive", true], ]); console.log(user.get("name")); // type: `string | undefined` console.log(user.get("age")); // type: `number | undefined` console.log(user.get("isActive")); // type: `boolean | undefined` ``` --- ###### Respect the given type ```ts import { getTypedMap } from "@alessiofrittoli/web-utils"; interface User { name: string; age: number; isActive: boolean; banned?: boolean; } const user = getTypedMap<User, false>([ ["name", "Foo"], ["age", 27], ["isActive", true], ]); console.log(user.get("name")); // type: `string` console.log(user.get("age")); // type: `number` console.log(user.get("isActive")); // type: `boolean` console.log(user.get("banned")); // type: `boolean | undefined` ``` </details> --- #### Promises utilities ##### `sleep` Await a void Promise that resolves after the given time. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | -------------------------------------------------------------- | | `time` | `number` | The sleep time in milliseconds after the Promise get resolved. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `Promise<void>` A new Promise which get resolved after the specified time. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ```ts import { sleep } from "@alessiofrittoli/web-utils"; const fn = async () => { // ... await sleep(2000); // ... }; ``` </details> --- ##### `deferTask` Defer task so main-thread is not blocked in order to quickly paint and respond to user interaction. <details> <summary style="cursor:pointer">Type Parameters</summary> | Parameter | Description | | --------- | ------------------------------------------------------------------------------------------------- | | `T` | The task function definition. `unknown` types will be inherited by your function type definition. | | `U` | The task function arguments. `unknown` types will be inherited by your function type. | </details> --- <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | ---- | ------------------------------------------------ | | `task` | `T` | The task callable function. | | `...args` | `U` | Arguments required by the given `task` function. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `Promise<Awaited<ReturnType<T>>>` A new Promise which returns the `task` result once fulfilled. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Basic usage ```ts import { deferTask } from '@alessiofrittoli/web-utils' const myLongTask = () => { ... } button.addEventListener( 'click', () => { deferTask( myLongTask ) } ) ``` --- ###### With custom arguments ```ts import { deferTask } from '@alessiofrittoli/web-utils' const myLongTask = ( target: HTMLButtonElement ) => { ... } button.addEventListener( 'click', event => { const target = event.target as HTMLButtonElement deferTask( myLongTask, target ) } ) ``` </details> --- ##### `deferCallback` Defer task handler so main-thread is not blocked in order to quickly paint and respond to user interaction. <details> <summary style="cursor:pointer">Type Parameters</summary> | Parameter | Description | | --------- | ------------------------------------------------------------------------------------------------- | | `T` | The task function definition. `unknown` types will be inherited by your function type definition. | | `U` | The task function arguments. `unknown` types will be inherited by your function type. | </details> --- <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | ---- | --------------------------- | | `task` | `T` | The task callable function. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `( ...args: U ) => Promise<Awaited<ReturnType<T>>>` A new handler which returns a new Promise that returns the `task` result once fulfilled. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ```ts import { deferCallback } from '@alessiofrittoli/web-utils' const myLongTask = ( event: Event ) => { ... } button.addEventListener( 'click', deferCallback( myLongTask ) ) ``` </details> --- #### Strings utilities ##### `ucFirst` Make first letter uppercase. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | ---------------------------- | | `input` | `string` | The input string to convert. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `string` The processed string. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ```ts import { ucFirst } from "@alessiofrittoli/web-utils"; console.log(ucFirst("String value")); // Outputs: 'String value' console.log(ucFirst("string value")); // Outputs: 'String value' ``` </details> --- ##### `lcFirst` Make first letter lowercase. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | ---------------------------- | | `input` | `string` | The input string to convert. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `string` The processed string. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ```ts import { lcFirst } from "@alessiofrittoli/web-utils"; console.log(lcFirst("String value")); // Outputs: 'string value' console.log(lcFirst("string value")); // Outputs: 'string value' ``` </details> --- ##### `toCamelCase` Convert string to camelCase. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | ---------------------------- | | `input` | `string` | The input string to convert. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `string` The converted string to camelCase. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ```ts import { toCamelCase } from "@alessiofrittoli/web-utils"; console.log(toCamelCase("font-family")); // Outputs: 'fontFamily' console.log(toCamelCase("background-color")); // Outputs: 'backgroundColor' console.log(toCamelCase("-webkit-align-content")); // Outputs: 'WebkitAlignContent' console.log(toCamelCase("some value")); // Outputs: 'someValue' console.log(toCamelCase("some_value")); // Outputs: 'someValue' console.log(toCamelCase("some value_with mixed_Cases")); // Outputs: 'someValueWithMixedCases' console.log(toCamelCase("-string@with#special$characters")); // Outputs: 'StringWithSpecialCharacters' ``` </details> --- ##### `toKebabCase` Convert string to kebab-case string. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | ---------------------------- | | `input` | `string` | The input string to convert. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `string` The converted string to kebab-case. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ```ts import { toKebabCase } from "@alessiofrittoli/web-utils"; console.log(toKebabCase("fontFamily")); // Outputs: 'font-family' console.log(toKebabCase("backgroundColor")); // Outputs: 'background-color' console.log(toKebabCase("string with spaces")); // Outputs: 'string-with-spaces' console.log(toKebabCase("string_with_underscores")); // Outputs: 'string-with-underscores' console.log(toKebabCase("WebkitAlignContent")); // Outputs: '-webkit-align-content' console.log(toKebabCase("some value_with mixed_Cases")); // Outputs: 'some-value-with-mixed-cases' console.log(toKebabCase("-string@with#special$characters")); // Outputs: '-string-with-special-characters ``` </details> --- ##### `stringifyValue` Stringify value. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | ----- | ----------------------- | | `input` | `any` | The value to stringify. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `string` The stringified `input`. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ```ts import { stringifyValue } from "@alessiofrittoli/web-utils"; console.log(stringifyValue(new Date("Sat, 20 Apr 2025 16:20:00 GMT"))); // Outputs: '2025-04-20T16:20:00.000Z' console.log(stringifyValue(null)); // Outputs: 'null' console.log(stringifyValue({ prop: "value", prop2: true })); // Outputs: '{"prop":"value","prop2":true}' console.log(stringifyValue([1, 2, true, null, () => {}])); // Outputs: '[1,2,true,null,null]' console.log( stringifyValue( new Map([ ["key", "value"], ["key2", "value"], ]), ), ); // Outputs: '[["key","value"],["key2","value"]]' console.log( stringifyValue( new Headers({ key: "value", key2: "value", }), ), ); // Outputs: '[["key","value"],["key2","value"]]' console.log(stringifyValue(true)); // Outputs: 'true' console.log(stringifyValue(false)); // Outputs: 'false' console.log(stringifyValue(0)); // Outputs: '0' console.log(stringifyValue(420)); // Outputs: '420' console.log(stringifyValue(undefined)); // Outputs: '' console.log(stringifyValue(() => {})); // Outputs: '' console.log(stringifyValue(new Promise<void>((resolve) => resolve()))); // Outputs: '' ``` </details> --- ##### `parseValue` Parse stringified value. <details> <summary style="cursor:pointer">Type parameters</summary> | Parameter | Description | | --------- | --------------------------------- | | `T` | The expected returned value type. | </details> --- <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | ------------------- | | `input` | `string` | The value to parse. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `T | undefined` - The parsed `input`. - `undefined` if no `input` or empty `string` is given. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ```ts import { parseValue } from "@alessiofrittoli/web-utils"; console.log(parseValue<Date>(stringifyValue(new Date()))); // Outputs: current Date object. console.log(parseValue<number>("12345")); // Outputs: 12345 console.log(parseValue()); // Outputs: undefined console.log(parseValue(" ")); // Outputs: undefined console.log(parseValue<true>(stringifyValue(true))); // Outputs: true console.log(parseValue(stringifyValue({ key: "value" }))); // Outputs: { key: 'value' } console.log(parseValue(stringifyValue([1, 2, 3, 4, 5]))); // Outputs: [ 1, 2, 3, 4, 5 ] console.log(parseValue("String value")); // Outputs: 'String value' ``` </details> --- ##### `parameterized` Creates a parameterized string with placeholder values. ###### Types ###### `ParameterizedValue` Represents a value that can be used as a parameter in string operations. ```ts type ParameterizedValue = string | boolean | number | bigint; ``` ###### `Parameterized` Represents a parameterized string with its corresponding values. ```ts type Parameterized = [string, ParameterizedValue[]]; ``` <details> <summary style="cursor:pointer">Examples</summary> ```ts import { parameterized } from "@alessiofrittoli/web-utils"; const data = { value: "parameterized", }; console.log(parameterized`My string with ${data.value} values.`); // [ 'My string with ? values.', [ 'parameterized' ] ] ``` </details> --- #### Types utilities ⚠️ Docs coming soon --- #### Validation utilities ⚠️ Docs coming soon #### Objects utilities ⚠️ Docs coming soon --- #### Browser API utilities ##### `getMediaMatches` Safely executes `window.matchMedia()` in server and browser environments. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Description | | --------- | -------- | -------------------------------- | | `query` | `string` | The Media Query string to check. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `boolean` - `false` if `window` is not defined or if the `document` currently doesn't matches the given `query`. - `true` otherwise. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Check if current device is landscape oriented ```ts import { getMediaMatches } from "@alessiofrittoli/web-utils"; console.log(!getMediaMatches("(orientation:portrait)")); ``` </details> --- ##### `openBrowserPopUp` Opens a webpage in a browser PopUp. The `openBrowserPopUp` uses [`Window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) under the hood, but provides default options to make your work easier. <details> <summary style="cursor:pointer">Parameters</summary> | Parameter | Type | Default | Description | | ------------------ | ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `options` | `OpenBrowserPopUpOptions` | - | An object defining custom PopUp options. | | `options.url` | `UrlInput` | - | The URL or path of the resource to be loaded. See [UrlInput](https://github.com/alessiofrittoli/url-utils?tab=readme-ov-file#urlinput) for more info about accepted formats. | | `options.width` | `number` | `600` | The PopUp width. | | `options.height` | `number` | `800` | The PopUp height. | | `options.context` | `string` | - | A string, without whitespace, specifying the name of the browsing context the resource is being loaded into. | | `options.features` | `OptionsFeatures` | - | Additional custom PopUp features. | </details> --- <details> <summary style="cursor:pointer">Returns</summary> Type: `WindowProxy | null` - a `WindowProxy` object is returned if the browser successfully opens the new browsing context. - `null` is returned if the browser fails to open the new browsing context, for example because it was blocked by a browser popup blocker. </details> --- <details> <summary style="cursor:pointer">Usage</summary> ###### Re-focus a previously opened popup ```ts import { openBrowserPopUp } from "@alessiofrittoli/web-utils"; let windowProxy: WindowProxy | null = null; const clickHandler = () => { if (windowProxy && !windowProxy.closed) { return windowProxy.focus(); } windowProxy = openBrowserPopUp({ url: { pathname: "/", query: { key: "value" }, }, }); }; ``` --- ###### Re-use a popup ```ts import { openBrowserPopUp } from "@alessiofrittoli/web-utils"; const clickHandler = () => { openBrowserPopUp({ context: "some-context-name", url: { pathname: "/", query: { key: "value" }, }, }); }; const clickHandler2 = () => { openBrowserPopUp({ context: "some-context-name", url: "/other-path", }); }; ``` </details> --- ##### Document Picture-in-Picture ###### Types ###### `OpenDocumentPictureInPictureOptions` Defines configuration options for opening a Document Picture-in-Picture window. <details> <summary style="cursor:pointer">Properties</summary> | Property | Type | Default | Description | | ------------------------------ | ----------------- | -------------- | ------------------------------------------------------------------------------------------ | | `sizes` | `InputDimensions` | `[ 250, 250 ]` | A tuple defining non-negative numbers representing the width and the height to set | | | | | for the Picture-in-Picture window's viewport, in pixels. | | | | | - See [`InputDimensions`](#inputdimensions) type for a list of possible values. | | `disallowReturnToOpener` | `boolean` | `false` | Hints to the browser that it should not display a UI control that enables the | | | | | user to return to the originating tab and close the Picture-in-Picture window. | | `preferInitialWindowPlacement` | `boolean` | `false` | Defines whether the Picture-in-Picture window will always appear back at the | | | | | position and size it initially opened at, when it is closed and then reopened. | | | | | By contrast, if `preferInitialWindowPlacement` is `false` the | | | | | Picture-in-Picture window's size and position will be remembered when closed | | | | | and reopened — it will reopen at its previous position and size, | | | | | for example as set by the user. | | `styles` | `Styles` | - | Custom styles to load inside the Picture-in-Picture window. | | | | | - See [`Styles`](#styles) type for a list of possible values. | | | | | ⚠️ To keep consistent styling with your web-app, document styles are automatically cloned. | | `onQuit` | `() => void` | - | A callback to execute when Picture-in-Picture window is closed. | </details> --- ###### `OpenDocumentPictureInPicture` Defines the returned result of opening a Document Picture-in-Picture window. <details> <summary style="cursor:pointer">Properties</summary> | Property | Type | Description | | -------- | -------- | ------------------------------------------------------------------- | | `window` | `Window` | The browsing context inside the Document Picture-in-Picture window. | </details> --- ###### `isDocumentPictureInPictureSupported` Checks if the Document Picture-in-Picture API is supported by the current browser. <details> <summary style="cursor:pointer">Returns</summary> Type: `boolean` - `true` if Document Picture-in-Picture is supported. - `false` otherwise. </details> --- <details> <summary style="cursor:pointer">Examples</summary> ```ts import { isDocumentPictureInPictureSupported } from "@alessiofrittoli/web-utils"; if ( isDocumentPictureInPictureSupported() ) { ... } ``` </details> --- ###### `requiresDocumentPictureInPictureAPI` Validates that the Document Picture-in-Picture API is supported by the current browser. - Throws a new `Exception` with code `ErrorCode.DOCUMENT_PIP_NOT_SUPPORTED` if the Document Picture-in-Picture API is not supported. <details> <summary style="cursor:pointer">Examples</summary> ```ts import { Exception } from "@alessiofrittoli/exception"; import { requiresDocumentPictureInPictureAPI, ErrorCode } from "@alessiofrittoli/web-utils"; const myFunction = () => { requiresDocumentPictureInPictureAPI