import { useCallback, useEffect, useRef } from "react";
export function useRafThrottle(callback) {
const callbackRef = useRef(callback);
const frameRef = useRef(null);
const latestArgs = useRef([]);
import { useCallback, useEffect, useState } from 'react';
function parseSearch(search) {
const params = new URLSearchParams(search);
const out = {};
for (const [key, value] of params.entries()) out[key] = value;
import { useCallback, useEffect, useRef, useState } from 'react';
export function usePaginatedFetch(fetchPage, { pageSize = 20 } = {}) {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
import { useMemo } from "react";
export type SortDir = "asc" | "desc";
export interface SortConfig<T> {
key: keyof T;
dir: SortDir;
import { useEffect, useState } from "react";
export function useDebouncedValue(value, delay = 300) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
export const initialCart = { items: {} };
export function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM': {
const { product, qty = 1 } = action;
const DEFAULTS = {
baseDelay: 300,
maxDelay: 8000,
factor: 2,
jitter: 0.5,
};
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { fetchCurrentUser, postLogin, postLogout } from './api';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const BASE_URL = "/api/search";
export async function searchProducts(query, { signal } = {}) {
const params = new URLSearchParams({ q: query, limit: "10" });
const res = await fetch(`${BASE_URL}?${params}`, {
signal,
import { useCallback, useEffect, useReducer } from "react";
type State = { secondsLeft: number; isRunning: boolean };
type Action =
| { type: "start"; seconds: number }
| { type: "tick" }
export const MAX_VISIBLE = 4;
const DEFAULTS = {
variant: "info",
duration: 4000,
};
export interface UseClipboardResult {
copied: boolean;
error: Error | null;
copy: (value: string) => Promise<boolean>;
}