Utility Functions

A collection of small, reusable utility functions to help with common programming tasks and enhance your development workflow.

cn (Class Name Utility)

A utility function for conditionally joining Tailwind CSS class names together.

Code

1import { type ClassValue, clsx } from "clsx"
2import { twMerge } from "tailwind-merge"
3
4export function cn(...inputs: ClassValue[]) {
5  return twMerge(clsx(inputs))
6}

Usage Example

import { cn } from "@/lib/utils" // Assuming cn is in lib/utils.ts export function CnExample() { const isActive = true const hasError = false return ( <div className={cn( "p-4 border rounded-md", isActive && "bg-npui-primary text-npui-primary-foreground", hasError ? "border-destructive" : "border-npui-border" )}> This div uses `cn` for dynamic styling. </div> ) }

formatDate

Formats a Date object into a human-readable string with customizable options.

Code

1/**
2 * Formats a Date object into a readable string.
3 * @param date The Date object to format.
4 * @param options Optional Intl.DateTimeFormatOptions for customization.
5 * @returns The formatted date string.
6 */
7export function formatDate(date: Date, options?: Intl.DateTimeFormatOptions): string {
8  const defaultOptions: Intl.DateTimeFormatOptions = {
9    year: "numeric",
10    month: "long",
11    day: "numeric",
12  }
13  return new Intl.DateTimeFormat("en-US", options || defaultOptions).format(date)
14}

Usage Example

import { formatDate } from "@/lib/utils/format-date" // Assuming path export function FormatDateExample() { const today = new Date() const customDate = new Date("2023-01-15T10:30:00Z") return ( <div className="space-y-2"> <p>Today: {formatDate(today)}</p> <p>Custom Date (short): {formatDate(customDate, { year: '2-digit', month: 'short', day: '2-digit' })}</p> <p>Custom Date (full): {formatDate(customDate, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' })}</p> </div> ) }

capitalize

Capitalizes the first letter of a given string.

Code

1/**
2 * Capitalizes the first letter of a string.
3 * @param str The input string.
4 * @returns The string with its first letter capitalized.
5 */
6export function capitalize(str: string): string {
7  if (!str) return ""
8  return str.charAt(0).toUpperCase() + str.slice(1)
9}

Usage Example

import { capitalize } from "@/lib/utils/capitalize" // Assuming path import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { useState } from "react" export function CapitalizeExample() { const [text, setText] = useState("hello world") return ( <div className="space-y-4"> <Label htmlFor="capitalize-input">Enter text</Label> <Input id="capitalize-input" value={text} onChange={(e) => setText(e.target.value)} placeholder="Type something..." /> <p>Capitalized: {capitalize(text)}</p> </div> ) }

truncateString

Truncates a string to a specified maximum length, adding an ellipsis if it's cut short.

Code

1/**
2 * Truncates a string to a specified length, adding an ellipsis if truncated.
3 * @param str The input string.
4 * @param maxLength The maximum length of the string before truncation.
5 * @returns The truncated string.
6 */
7export function truncateString(str: string, maxLength: number): string {
8  if (str.length <= maxLength) {
9    return str
10  }
11  return str.slice(0, maxLength) + "..."
12}

Usage Example

import { truncateString } from "@/lib/utils/truncate-string" // Assuming path export function TruncateStringExample() { const longText = "This is a very long sentence that needs to be truncated for display purposes." const shortText = "Short text." return ( <div className="space-y-2"> <p>Original: {longText}</p> <p>Truncated (20 chars): {truncateString(longText, 20)}</p> <p>Truncated (50 chars): {truncateString(longText, 50)}</p> <p>Short text: {truncateString(shortText, 20)}</p> </div> ) }

debounce (Function)

Creates a debounced version of a function, delaying its execution until a pause in calls.

Code

1/**
2 * Creates a debounced function that delays invoking `func` until after `wait` milliseconds
3 * have passed since the last time the debounced function was invoked.
4 * @param func The function to debounce.
5 * @param wait The number of milliseconds to delay.
6 * @returns The new debounced function.
7 */
8export function debounce<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void {
9  let timeout: NodeJS.Timeout | null = null
10
11  return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
12    const context = this
13    const later = () => {
14      timeout = null
15      func.apply(context, args)
16    }
17    if (timeout) {
18      clearTimeout(timeout)
19    }
20    timeout = setTimeout(later, wait)
21  }
22}

Usage Example

import { debounce } from "@/lib/utils/debounce" // Assuming path import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { useState, useCallback } from "react" export function DebounceFunctionExample() { const [inputValue, setInputValue] = useState("") const [displayValue, setDisplayValue] = useState("") const debouncedSetDisplayValue = useCallback( debounce((text: string) => { setDisplayValue(text) }, 500), [], ) const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { setInputValue(e.target.value) debouncedSetDisplayValue(e.target.value) } return ( <div className="space-y-4"> <Label htmlFor="debounce-func-input">Type something (debounced display)</Label> <Input id="debounce-func-input" placeholder="Start typing..." value={inputValue} onChange={handleChange} /> <p>Live Input: {inputValue}</p> <p>Debounced Display: {displayValue}</p> </div> ) }

throttle

Creates a throttled version of a function, ensuring it's called at most once within a given time frame.

Code

1/**
2 * Creates a throttled function that only invokes `func` at most once per `wait` milliseconds.
3 * @param func The function to throttle.
4 * @param wait The number of milliseconds to throttle invocations to.
5 * @returns The new throttled function.
6 */
7export function throttle<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void {
8  let inThrottle: boolean
9  let lastFn: NodeJS.Timeout | null
10  let lastTime: number
11
12  return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
13    const context = this
14    if (!inThrottle) {
15      func.apply(context, args)
16      lastTime = Date.now()
17      inThrottle = true
18    } else {
19      if (lastFn) {
20        clearTimeout(lastFn)
21      }
22      lastFn = setTimeout(
23        function () {
24          if (Date.now() - lastTime >= wait) {
25            func.apply(context, args)
26            lastTime = Date.now()
27          }
28        },
29        Math.max(wait - (Date.now() - lastTime), 0),
30      )
31    }
32  }
33}

Usage Example

import { throttle } from "@/lib/utils/throttle" // Assuming path import { Button } from "@/components/ui/button" import { useState, useCallback } from "react" export function ThrottleExample() { const [count, setCount] = useState(0) const throttledIncrement = useCallback( throttle(() => { setCount((prev) => prev + 1) }, 1000), // Only increment once per second [], ) return ( <div className="space-y-4"> <p>Count: {count}</p> <Button onClick={throttledIncrement}>Rapid Click Me (Throttled)</Button> <p className="text-sm text-npui-muted-foreground"> Clicking rapidly will only increment the count once per second. </p> </div> ) }

isValidEmail

Performs a basic validation check to determine if a string is a valid email address.

Code

1/**
2 * Checks if a string is a valid email address using a simple regex.
3 * @param email The email string to validate.
4 * @returns True if the email is valid, false otherwise.
5 */
6export function isValidEmail(email: string): boolean {
7  const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/
8  return emailRegex.test(email)
9}

Usage Example

import { isValidEmail } from "@/lib/utils/is-valid-email" // Assuming path import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { useState } from "react" export function IsValidEmailExample() { const [email, setEmail] = useState("") const valid = isValidEmail(email) return ( <div className="space-y-4"> <Label htmlFor="email-input">Email Address</Label> <Input id="email-input" type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="test@example.com" /> {email && ( <p className={valid ? "text-green-500" : "text-destructive"}> {valid ? "Valid Email" : "Invalid Email"} </p> )} </div> ) }

generateRandomId

Generates a simple, unique string ID, optionally with a custom prefix.

Code

1/**
2 * Generates a simple unique ID string.
3 * @param prefix An optional prefix for the ID.
4 * @returns A unique ID string.
5 */
6export function generateRandomId(prefix: string = "id_"): string {
7  return prefix + Math.random().toString(36).substring(2, 9)
8}

Usage Example

import { generateRandomId } from "@/lib/utils/generate-random-id" // Assuming path import { Button } from "@/components/ui/button" import { useState } from "react" export function GenerateRandomIdExample() { const [id, setId] = useState("") return ( <div className="space-y-4"> <Button onClick={() => setId(generateRandomId("item-"))}>Generate New ID</Button> {id && <p>Generated ID: {id}</p>} </div> ) }

clamp

Restricts a number to be within a specified minimum and maximum range.

Code

1/**
2 * Clamps a number between an upper and lower bound.
3 * @param num The number to clamp.
4 * @param min The lower bound.
5 * @param max The upper bound.
6 * @returns The clamped number.
7 */
8export function clamp(num: number, min: number, max: number): number {
9  return Math.min(Math.max(num, min), max)
10}

Usage Example

import { clamp } from "@/lib/utils/clamp" // Assuming path import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { useState } from "react" export function ClampExample() { const [value, setValue] = useState(50) const clampedValue = clamp(value, 0, 100) return ( <div className="space-y-4"> <Label htmlFor="clamp-input">Enter a number (clamped between 0 and 100)</Label> <Input id="clamp-input" type="number" value={value} onChange={(e) => setValue(Number(e.target.value))} /> <p>Original Value: {value}</p> <p>Clamped Value: {clampedValue}</p> </div> ) }

hexToRgba

Converts a hexadecimal color string to an RGBA color string, allowing for transparency.

Code

1/**
2 * Converts a hexadecimal color string to an RGBA color string.
3 * @param hex The hexadecimal color string (e.g., "#RRGGBB" or "#RGB").
4 * @param alpha The alpha transparency value (0-1).
5 * @returns The RGBA color string (e.g., "rgba(255, 0, 0, 0.5)").
6 */
7export function hexToRgba(hex: string, alpha: number = 1): string | null {
8  let r = 0,
9    g = 0,
10    b = 0
11  // Handle #RGB format
12  if (hex.length === 4) {
13    r = parseInt(hex[1] + hex[1], 16)
14    g = parseInt(hex[2] + hex[2], 16)
15    b = parseInt(hex[3] + hex[3], 16)
16  }
17  // Handle #RRGGBB format
18  else if (hex.length === 7) {
19    r = parseInt(hex.substring(1, 3), 16)
20    g = parseInt(hex.substring(3, 5), 16)
21    b = parseInt(hex.substring(5, 7), 16)
22  } else {
23    return null // Invalid hex format
24  }
25  return `rgba(${r}, ${g}, ${b}, ${alpha})`
26}

Usage Example

import { hexToRgba } from "@/lib/utils/hex-to-rgba" // Assuming path import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { useState } from "react" export function HexToRgbaExample() { const [hex, setHex] = useState("#FF7F00") const [alpha, setAlpha] = useState(1) const rgba = hexToRgba(hex, alpha) return ( <div className="space-y-4"> <Label htmlFor="hex-input">Hex Color</Label> <Input id="hex-input" value={hex} onChange={(e) => setHex(e.target.value)} /> <Label htmlFor="alpha-input">Alpha (0-1)</Label> <Input id="alpha-input" type="number" step="0.1" min="0" max="1" value={alpha} onChange={(e) => setAlpha(Number(e.target.value))} /> <p>RGBA: {rgba || "Invalid Hex"}</p> {rgba && ( <div className="w-24 h-24 rounded-md" style={{ backgroundColor: rgba }} ></div> )} </div> ) }

getRandomItem

Selects and returns a random item from an array.

Code

1/**
2 * Returns a random item from an array.
3 * @param arr The input array.
4 * @returns A random item from the array, or undefined if the array is empty.
5 */
6export function getRandomItem<T>(arr: T[]): T | undefined {
7  if (arr.length === 0) {
8    return undefined
9  }
10  const randomIndex = Math.floor(Math.random() * arr.length)
11  return arr[randomIndex]
12}

Usage Example

import { getRandomItem } from "@/lib/utils/get-random-item" // Assuming path import { Button } from "@/components/ui/button" import { useState } from "react" export function GetRandomItemExample() { const items = ["Apple", "Banana", "Cherry", "Date", "Elderberry"] const [randomFruit, setRandomFruit] = useState<string | undefined>(undefined) return ( <div className="space-y-4"> <p>Fruits: {items.join(", ")}</p> <Button onClick={() => setRandomFruit(getRandomItem(items))}>Pick Random Fruit</Button> {randomFruit && <p>Randomly picked: {randomFruit}</p>} </div> ) }