q-js-utils
v1.3.6
Published
A collection of JavaScript utilities.
Maintainers
Readme
q-js-utils
A collection of JavaScript utilities.
Installation
npm install q-js-utilsOr
yarn add q-js-utilsUsage
For the entire library (convenience, but potentially larger bundle if not tree-shaken by consumer's bundler):
import { darkOrLight, str2Hex, getInitials } from 'q-js-utils';
// Or
// import * as utils from 'q-js-utils';
const isDark = darkOrLight('#000');
const nameToHex = str2Hex('Muhamad Husein');
const initialName = getInitials('Muhamad Husein');
console.log(isDark); // true
console.log(nameToHex); // f529de
console.log(initialName); // MHFor specific modules (recommended for tree-shaking):
import { darkOrLight } from 'q-js-utils/darkOrLight';
import { str2Hex } from 'q-js-utils/str2Hex';
import { getInitials } from 'q-js-utils/getInitials';Utilities
For numeric data
import { isNumber } from 'q-js-utils/isNumber';
import { isNegative } from 'q-js-utils/isNegative';
import { padWithLeadingZeros } from 'q-js-utils/padWithLeadingZeros';
const one = 1;
const minus = -1;
console.log(isNumber(one)); // true
console.log(isNegative(one)); // false
console.log(isNegative(minus)); // true
console.log(padWithLeadingZeros(one)); // '01'debounce
Debouncing an input for an autocomplete search
import { debounce } from 'q-js-utils/debounce';
const searchInput = document.getElementById('searchInput') as HTMLInputElement;
const performSearch = (query: string) => {
console.log(`Performing search for: ${query}`);
// In a real app, you would make an API call here
};
const debouncedSearch = debounce(performSearch, 300); // Wait 300ms after last keypress
if (searchInput) {
searchInput.addEventListener('input', (event) => {
const target = event.target as HTMLInputElement;
debouncedSearch(target.value);
});
}Debouncing a window resize event
import { debounce } from 'q-js-utils/debounce';
const handleResize = () => {
console.log(`Window resized to: ${window.innerWidth}x${window.innerHeight}`);
};
// Wait 250ms after resizing stops
const debouncedResize = debounce(handleResize, 250);
window.addEventListener('resize', debouncedResize);debounceAdvanced
Resetting a Form Before Debounced Submission
import { debounceAdvanced } from 'q-js-utils/debounceAdvanced';
const submitForm = debounceAdvanced(
(formData: { email: string }) => console.log("Submitting:", formData),
1000
);
// User clicks submit but then clicks "Reset"
submitForm({ email: "[email protected]" });
submitForm.cancel(); // Stops the submission if the form was resetAvoiding Unnecessary Resize/Scroll Handlers
import { debounceAdvanced } from 'q-js-utils/debounceAdvanced';
const logScrollPosition = debounceAdvanced(
() => console.log("Current scroll Y:", window.scrollY),
200
);
window.addEventListener("scroll", logScrollPosition);
// When navigating away, cancel pending calls
window.removeEventListener("scroll", logScrollPosition);
logScrollPosition.cancel(); // CleanupGame Input Handling (Cancel on Player Death)
import { debounceAdvanced } from 'q-js-utils/debounceAdvanced';
const shootBullet = debounceAdvanced(
() => console.log("🔥 Pew!"),
300,
{ leading: true } // Immediate first shot
);
shootBullet(); // Fires instantly
shootBullet(); // Queued for 300ms later...
// Player dies before the next shot
shootBullet.cancel(); // Prevents queued shotsthrottle
Basic usage
import { throttle } from 'q-js-utils/throttle';
const throttledScroll = throttle((position: number) => {
console.log('Current scroll position:', position);
}, 100);
window.addEventListener('scroll', () => throttledScroll(window.scrollY));With default wait time (300ms)
import { throttle } from 'q-js-utils/throttle';
const throttledClick = throttle(() => console.log('Clicked!'));
button.addEventListener('click', throttledClick);throttleAdvanced
Cancellation Support (cancel())
import { throttleAdvanced } from 'q-js-utils/throttleAdvanced';
const throttledScroll = throttleAdvanced(handleScroll, 100);
window.addEventListener('scroll', throttledScroll);
// Later...
throttledScroll.cancel(); // Stops any pending executionImmediate Execution (flush())
import { throttleAdvanced } from 'q-js-utils/throttleAdvanced';
const throttledApiCall = throttleAdvanced(fetchData, 1000);
throttledApiCall("query");
// Force execute if waiting
throttledApiCall.flush();Pending Check (pending())
if (throttledFn.pending()) {
console.log("Waiting to execute...");
}Example Use Cases:
Cancellable Scroll Handler
import { throttleAdvanced } from 'q-js-utils/throttleAdvanced';
const scrollHandler = throttleAdvanced((position: number) => {
console.log("Current scroll:", position);
}, 200);
window.addEventListener('scroll', () => scrollHandler(window.scrollY));
// When leaving the page
window.addEventListener('beforeunload', () => {
scrollHandler.cancel(); // Cleanup
});Throttled API Calls with Manual Flush
import { throttleAdvanced } from 'q-js-utils/throttleAdvanced';
const searchAPI = throttleAdvanced(async (query: string) => {
const results = await fetch(`/search?q=${query}`);
displayResults(results);
}, 500);
searchInput.addEventListener('input', (e) => {
searchAPI(e.target.value);
});
// "Search Now" button forces execution
searchButton.addEventListener('click', () => {
searchAPI.flush();
});Game Loop with Cancellation
import { throttleAdvanced } from 'q-js-utils/throttleAdvanced';
const gameUpdate = throttleAdvanced((deltaTime: number) => {
updatePlayerPosition(deltaTime);
}, 16); // ~60fps
gameLoop.on('update', gameUpdate);
// When game pauses
pauseButton.addEventListener('click', () => {
gameUpdate.cancel(); // Stop pending updates
});request
import { request } from 'q-js-utils/request';
try {
const todo = await request('https://jsonplaceholder.typicode.com/todos/1');
console.log('request Todo:', todo);
} catch (error) {
console.error('request Error:', error);
}
console.log('Download Progress');
try {
const blob = await request('https://httpbin.org/bytes/1048576', {
// @ts-ignore
// responseType: "hell",
responseType: "blob",
onProgress: (progress) => {
console.log('progress: ', progress);
if (progress.total) {
console.log(
`Progress: ${progress.loaded} / ${progress.total} bytes (${(
progress.progress! * 100
).toFixed(2)}%)`,
);
} else {
console.log(`Progress: ${progress.loaded} bytes loaded (total unknown)`);
}
},
});
console.log('Download blob: ', blob);
console.log('Download complete. Blob size: ', blob?.size, 'bytes');
} catch (error: any) {
console.error('4. Download Progress Error: ', error);
for(let err in error){
console.error('err: ', err);
}
}nextId
Generates a unique, sequentially incremented string ID with an optional prefix. Each call increments an internal counter to ensure uniqueness.
import { nextId } from '../src/nextId';
console.log(`nextId to ${nextId()}`);
console.log(`nextId to ${nextId()}`);
console.log(`nextId to ${nextId('x')}`);cached
import { cache } from 'q-js-utils/cache';
const sayHi = cache(name => 'Hi, ' + name);darkOrLight
import { darkOrLight } from 'q-js-utils/darkOrLight';
const isDark = darkOrLight('#000');str2Hex
import { str2Hex } from 'q-js-utils/str2Hex';
const nameToHex = str2Hex('Muhamad Husein');getInitials
import { getInitials } from 'q-js-utils/getInitials';
const initialName = getInitials('Muhamad Husein');obj2FormData
import { obj2FormData } from 'q-js-utils/obj2FormData';
const objData = {
name: "Muhamad Husein",
email: "[email protected]"
};
const dataForm = obj2FormData(objData);isEqual
Deeply compares two values to determine if they are equal. Handles primitives, arrays, objects, Maps, Sets, Dates, RegExps, and circular references.
Can be significantly faster than Lodash's _.isEqual() in most cases, while still handling all the same edge cases.
import { isEqual } from 'q-js-utils/isEqual';
isEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }) // true
isEqual({ a: 1 }, { a: 1, b: undefined }) // false
isEqual({ a: 1, b: [2, 3] }, { b: [2, 3], a: 1 }) // true
isEqual(NaN, NaN) // truecn
Joins class names together, filtering out falsy values.
@param {...(string | boolean | null | undefined)} classes - Class names or conditional expressions.
returns string Combined class names as a single string, or undefined (to prevent class="" not render in node).
import { cn } from 'q-js-utils/cn';
const isActive = true;
const hasError = false;
const emptyString = '';
const zero = 0;
const nullVar = null;
const undefinedConst = undefined;
let undefinedLet;
// Returns: "btn active" (when isActive is true and hasError is false)
cn('btn', isActive && 'active', hasError && 'error');
// undefined
cn(
hasError && 'error',
emptyString && 'emptyString',
zero && 'zero',
nullVar && 'nullVar',
undefinedConst && 'undefinedConst',
undefinedLet && 'undefinedLet',
);uuidv7
Generate a UUIDv7 (time-ordered, monotonic). RFC 9562: https://www.rfc-editor.org/rfc/rfc9562.html
import { uuidv7 } from 'q-js-utils/uuidv7';
// Lexicographically sorted even if generated in the same millisecond.
console.log('1. uuidv7:', uuidv7());
console.log('2. uuidv7:', uuidv7());
console.log('3. uuidv7:', uuidv7());
// Example output:
// 018f3fcd-04d1-7a8f-9f4a-1b33a5a2f7c9
// 018f3fcd-04d1-7a90-8a77-3a13f20dcbae
// 018f3fcd-04d1-7a91-81df-1d92ff45a4ecdownload
Triggers a file download from a given Blob or File object in modern browsers.
import { download } from 'q-js-utils/download';
// Basic
download(text);
// Custom filename
download(text, {
name: "greeting.txt"
});
// Custom timeout to cleanup createObjectURL
download(text, {
timeout: 1000,
});
// Whether to append anchor to DOM for Safari compatibility
download(text, {
append: true,
});
// All
download(text, {
name: "greeting.txt",
timeout: 1000,
append: true,
});shape
Shapes an object by picking or omitting specified keys, with TypeScript inferring exact key types.
import { shape } from 'q-js-utils/shape';
const user = {
id: 1,
name: "Husein",
email: "[email protected]",
role: "admin",
};
const pick = shape(user, ["id", "name"] as const); // { id: 1; name: "Husein" }
const omit = shape(user, ["id", "name"] as const, true); // { email: "[email protected]"; role: "admin" }capitalize
Capitalizes the first letter.
import { capitalize } from 'q-js-utils/capitalize';
console.log(capitalize("husein")); // Husein
console.log(capitalize("élève")); // "Élève"
console.log(capitalize("mañana")); // "Mañana"