UNPKG

@use-pico/cls

Version:

Type-safe, composable styling system for React, Vue, Svelte, and vanilla JS

95 lines (92 loc) 3.09 kB
import { type DependencyList, useMemo } from "react"; import type { Cls } from "../types/Cls"; import type { Contract } from "../types/Contract"; import type { Token } from "../types/Token"; import type { Tweak } from "../types/Tweak"; import type { Variant } from "../types/Variant"; import { useTokenContext } from "./useTokenContext"; import { useVariantContext } from "./useVariantContext"; /** * Memoized version of `useCls` with explicit dependency control for performance optimization. * * **Tweak Precedence (highest to lowest):** * 1. **User tweak** - Direct user customization passed as parameter * 2. **Context tweak** - Automatically merged from TokenContext and VariantContext * * **Context Integration:** * - Automatically subscribes to `TokenContext` for token overrides * - Automatically subscribes to `VariantContext` for variant overrides * - Context values have lower precedence than user-provided tweaks * * **Memoization Control:** * - Uses `useMemo` with the provided dependency list * - Recreates the CLS kit only when dependencies change * - Essential for performance when tweaks depend on props or state * * **Tweak Merging:** * - Uses the `tweaks()` function with parameter-based syntax * - User tweak takes precedence over context values * - Undefined values are automatically filtered out * * @template TContract - CLS contract type defining tokens, slots, and variants * @param cls - CLS instance to create kit from * @param tweaks - Optional user-provided tweak (variant/slot/token/override) * @param deps - Dependency list controlling memoization (defaults to empty array) * @returns A memoized `Cls.Kit<TContract>` with slot functions (e.g., `slots.root()`) and resolved variants * * @example * ```tsx * // Basic usage with memoization based on props * const { slots, variant } = useClsMemo( * ButtonCls, * { variant: { size, tone } }, * [size, tone] // Re-memoize when size or tone changes * ); * ``` * * @example * ```tsx * // Complex tweak with multiple dependencies * const { slots, variant } = useClsMemo( * ButtonCls, * { * variant: { size, tone, disabled }, * slot: { root: { class: disabled ? ["opacity-50"] : [] } } * }, * [size, tone, disabled] // Re-memoize when any dependency changes * ); * ``` * * @example * ```tsx * // No user tweak - memoizes context-only result * const { slots, variant } = useClsMemo( * ButtonCls, * undefined, * [] // Never re-memoize (context changes handled internally) * ); * ``` */ export function useClsMemo<TContract extends Contract.Any>( cls: Cls.Type<TContract>, tweaks?: Tweak.Tweaks<TContract>, deps: DependencyList = [], ): Cls.Kit<TContract> { const token = useTokenContext() as Token.Optional<TContract>; const variant = useVariantContext() as Variant.Optional<TContract>; return useMemo( () => cls.create( /** * Context tweak has lowest priority */ { token, variant, }, tweaks, ), // biome-ignore lint/correctness/useExhaustiveDependencies: User driven deps, ); }