Misc utils
import { cx, copyToClipboard, downloadFile } from '@gg-software/utils';copyToClipboard and downloadFile are browser helpers — during SSR they safely no-op
(false / undefined).
cx
Join class names conditionally. Accepts strings, numbers, nested arrays and
{ className: condition } objects; falsy values are skipped.
cx('btn', isActive && 'btn--active', { 'btn--lg': large });
// → "btn btn--active btn--lg"
cx(['a', ['b', null]], 0, undefined, 'c'); // "a b c"copyToClipboard
Copy text to the clipboard — async Clipboard API with a legacy execCommand fallback.
Resolves to whether the copy succeeded.
const ok = await copyToClipboard('hello');
toast({ title: ok ? 'Copied' : 'Copy failed' });downloadFile
Trigger a browser download of a Blob or string — CSV/JSON exports.
downloadFile(csv, 'orders.csv', 'text/csv');
downloadFile(JSON.stringify(rows, null, 2), 'rows.json', 'application/json');
downloadFile(blob, 'photo.jpg'); // mimeType comes from the Blob| Parameter | Type | Default |
|---|---|---|
data | Blob | string | — |
filename | string | — |
mimeType | string (used for string data) | 'text/plain' |
parseQueryString
Query string → object. Repeated keys become arrays; a leading ? is tolerated.
parseQueryString('?q=shoes&page=2'); // { q: "shoes", page: "2" }
parseQueryString('tag=a&tag=b'); // { tag: ["a", "b"] }buildUrl
Append query params to a URL. null/undefined values are skipped, arrays repeat the key,
params already on the base are kept.
buildUrl('/search', { q: 'shoes', page: 2 }); // "/search?q=shoes&page=2"
buildUrl('/filter', { tag: ['a', 'b'], empty: null }); // "/filter?tag=a&tag=b"
buildUrl('https://api.example.com/items?v=1', { page: 2 });noop
A function that does nothing — the classic default callback.
const { onChange = noop } = props;