Skip to content

user-xxy/utils

Repository files navigation

@user-xxy/utils

Framework-agnostic utilities for browser-based web apps. Tree-shakeable, TypeScript-first, zero runtime dependencies. The request-cancellation helper accepts axios's CancelToken as an injected argument so you don't have to install axios unless you actually use it.

pnpm add @user-xxy/utils

Modules

number

import { formatNumber, formatToW, numberToChinese, moneyFormat, padZero, getLatencyColor } from '@user-xxy/utils'

formatNumber(1234567)        // "1,234,567"
formatToW(12345)             // "1.23w"
numberToChinese(42)          // "四十二"
moneyFormat('12.3')          // "12.30"
padZero(7)                   // "07"
getLatencyColor(120)         // "#FF8C00"

string

import { maskString, byteLength, graphemeLength, snakeToCamel, camelToSnake, bin2hex, compareVersion } from '@user-xxy/utils'

maskString('13812345678', { prefix: 3, suffix: 4 }) // "138***5678"
graphemeLength('👨‍👩‍👧')                                // 1
compareVersion('v1.2.3', '1.2.10')                  // -1

date

import { formatDate, formatRelativeDate, formatExpireTime, formatOverdueDuration, getDayRange } from '@user-xxy/utils'

formatDate(Date.now(), 'YYYY/MM/DD')                  // "2026/05/24"
formatRelativeDate(Date.now() - 86_400_000)           // "昨天 12:00"
formatExpireTime(Date.now() + 3 * 86_400_000)         // "3天后过期"
getDayRange([new Date(), new Date()])
// { start_time: '2026/05/24 00:00:00', end_time: '2026/05/24 23:59:59' }

function

import { debounce, throttle, sleep, retry, withLoading, runWithLoading } from '@user-xxy/utils'

const onResize = debounce(() => layout(), 200)
const onScroll = throttle(() => sync(), 100)
await sleep(500)
await retry(() => fetchUser(id), { retries: 3, backoff: 2 })

// withLoading decouples UI from business logic
const loadable = withLoading(api.save, {
  start: () => ElLoading.service({ text: '保存中...' }),
  stop:  (h) => h.close(),
})
await loadable(payload)

object

import { deepClone, filterEmpty, enumToOptions, findLabel, removeByIds, setSingleKeyTrue, groupBy } from '@user-xxy/utils'

filterEmpty({ a: 1, b: '', c: null })  // { a: 1 }
enumToOptions(Status)                  // [{ label, value }, ...]
removeByIds(items, [2, 4])
setSingleKeyTrue({ a: true, b: false, c: false }, 'b')

browser

import {
  isBrowser, isMobileDevice, isMobileViewport, isIOS, isWechat, isTouchSupported,
  getUrlParams, parseUrl, copyToClipboard, downloadBlob, downloadFile, setupRem, uuid,
} from '@user-xxy/utils'

await copyToClipboard('hello')
const stop = setupRem({ designWidth: 375, baseSize: 37.5 })
// later (e.g. unmount)
stop()

image

import { compressImage, imageToBase64, captureVideoFrame, pickFile } from '@user-xxy/utils'

const file = await pickFile({ accept: '.xlsx' })
const small = await compressImage(file as File, { targetSize: 500 })
const poster = await captureVideoFrame('https://x.com/v.mp4')

storage

import { createStorage, setItemWithExpiry, getItemWithExpiry } from '@user-xxy/utils'

const cache = createStorage({ prefix: 'app.' })
cache.set('user', { id: 1 })
cache.get<{ id: number }>('user')?.id

setItemWithExpiry('token', 'abc', 60_000)
const token = getItemWithExpiry<string>('token')

http — request-cancellation

import axios from 'axios'
import { createPendingRequests } from '@user-xxy/utils'

const pending = createPendingRequests(axios.CancelToken)
const instance = axios.create({ baseURL: '/api' })

instance.interceptors.request.use((cfg) => {
  pending.remove(cfg) // cancel a duplicate in flight
  pending.add(cfg)
  return cfg
})
instance.interceptors.response.use(
  (res) => { pending.remove(res.config); return res },
  (err) => { if (err?.config) pending.remove(err.config); return Promise.reject(err) },
)

wechat

import { getWxSignUrl, resetWxSignUrl, initWxJsSdk, wxScanQRCode } from '@user-xxy/utils'

const url = getWxSignUrl()        // iOS-safe (persists first URL)
const sig = await fetchSign(url)  // your backend

await initWxJsSdk({ appId, timestamp, nonceStr, signature: sig, jsApiList: ['scanQRCode'] })
const code = await wxScanQRCode()

validate

import { validators, inputFilters } from '@user-xxy/utils'

validators.mobileCN('13812345678')  // true
validators.email('a@b.cn')          // true

// in an input handler
input.value = inputFilters.decimal(input.value, 2)
input.value = inputFilters.integer(input.value)
input.value = inputFilters.percentInt(input.value)

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors