@codeleap/hooks 6.0.1 → 6.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/hooks",
3
- "version": "6.0.1",
3
+ "version": "6.1.2",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -9,19 +9,19 @@
9
9
  "directory": "packages/hooks"
10
10
  },
11
11
  "devDependencies": {
12
- "@codeleap/config": "6.0.1",
13
- "@codeleap/types": "6.0.1",
14
- "@codeleap/utils": "6.0.1",
15
- "@codeleap/logger": "6.0.1",
12
+ "@codeleap/config": "6.1.2",
13
+ "@codeleap/types": "6.1.2",
14
+ "@codeleap/utils": "6.1.2",
15
+ "@codeleap/logger": "6.1.2",
16
16
  "ts-node-dev": "1.1.8"
17
17
  },
18
18
  "scripts": {
19
19
  "build": "echo 'No build needed'"
20
20
  },
21
21
  "peerDependencies": {
22
- "@codeleap/types": "6.0.1",
23
- "@codeleap/utils": "6.0.1",
24
- "@codeleap/logger": "6.0.1",
22
+ "@codeleap/types": "6.1.2",
23
+ "@codeleap/utils": "6.1.2",
24
+ "@codeleap/logger": "6.1.2",
25
25
  "axios": "^1.7.9",
26
26
  "typescript": "5.5.2",
27
27
  "react": "19.1.0",
package/package.json.bak CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/hooks",
3
- "version": "6.0.1",
3
+ "version": "6.1.2",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
package/src/index.ts CHANGED
@@ -13,9 +13,7 @@ import {
13
13
 
14
14
  export * from './useConditionalState'
15
15
  export * from './usePromise'
16
- export * from './useListState'
17
16
  export * from './useUncontrolled'
18
- export * from './useCounter'
19
17
  export * from './useForceRender'
20
18
  export * from './useDebounce'
21
19
  export * from './useInterval'
@@ -34,6 +32,13 @@ export * from './usePartialState'
34
32
  export * from './useIsMounted'
35
33
  export * from './useComponentTestId'
36
34
  export * from './useId'
35
+ export * from './useAnimatedState'
36
+ export * from './useDebounceCallback'
37
+ export * from './useDerivedRef'
38
+ export * from './useDerivedState'
39
+ export * from './useFilteredList'
40
+ export * from './useLazyStore'
41
+ export * from './useOptions'
37
42
 
38
43
  export {
39
44
  useEffect,
package/src/onMount.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  import { useEffect } from 'react'
2
2
  import { AnyFunction } from '@codeleap/types'
3
3
 
4
+ /**
5
+ * Hook that runs a function once when the component mounts.
6
+ *
7
+ * @example
8
+ * onMount(() => {
9
+ * console.log('Component mounted')
10
+ * return () => console.log('Component unmounted')
11
+ * })
12
+ */
4
13
  export const onMount = (func: AnyFunction) => {
5
14
  useEffect(() => {
6
15
  return func()
package/src/onUpdate.ts CHANGED
@@ -1,6 +1,14 @@
1
1
  import { useEffect } from 'react'
2
2
  import { AnyFunction } from '@codeleap/types'
3
3
 
4
+ /**
5
+ * Hook that runs a function when specified dependencies change.
6
+ *
7
+ * @example
8
+ * onUpdate(() => {
9
+ * console.log('Count changed:', count)
10
+ * }, [count])
11
+ */
4
12
  export const onUpdate = (func: AnyFunction, listeners = []) => {
5
13
  useEffect(() => {
6
14
  return func()
@@ -0,0 +1,42 @@
1
+ import { useState, useCallback } from 'react'
2
+ import { SharedValue, useSharedValue, useDerivedValue, runOnJS, isSharedValue } from 'react-native-reanimated'
3
+
4
+ /**
5
+ * Hook that synchronizes a React state with a Reanimated SharedValue.
6
+ *
7
+ * @example
8
+ * const [value, setValue, sharedValue] = useAnimatedState(0)
9
+ * setValue(10) // Updates both React state and SharedValue
10
+ */
11
+ export function useAnimatedState<T>(initialValue: SharedValue<T> | T) {
12
+ const sharedValue = isSharedValue(initialValue)
13
+ ? initialValue
14
+ : useSharedValue<T>(initialValue)
15
+
16
+ const [value, _setValue] = useState<T>(
17
+ isSharedValue(initialValue) ? initialValue.value : initialValue,
18
+ )
19
+
20
+ useDerivedValue(() => {
21
+ runOnJS(_setValue)(sharedValue.value)
22
+ })
23
+
24
+ const setValue = useCallback((newValue: T) => {
25
+ sharedValue.value = newValue
26
+ if (isSharedValue(initialValue)) initialValue.value = newValue
27
+ _setValue(newValue)
28
+ }, [])
29
+
30
+ return [value, setValue, sharedValue] as const
31
+ }
32
+
33
+ /**
34
+ * Hook that extracts and synchronizes the value from a SharedValue.
35
+ *
36
+ * @example
37
+ * const value = useAnimatedValue(sharedValue)
38
+ */
39
+ export function useAnimatedValue<T>(initialValue: SharedValue<T> | T): T {
40
+ const [value] = useAnimatedState(initialValue)
41
+ return value
42
+ }
@@ -1,5 +1,13 @@
1
1
  import { useState } from 'react'
2
2
 
3
+ /**
4
+ * Hook that manages a boolean state with toggle functionality.
5
+ *
6
+ * @example
7
+ * const [isOpen, toggleOpen] = useBooleanToggle(false)
8
+ * toggleOpen() // Toggles the value
9
+ * toggleOpen(true) // Sets to true
10
+ */
3
11
  export function useBooleanToggle(initial: boolean) {
4
12
  const [v, setV] = useState(initial)
5
13
 
@@ -13,8 +13,7 @@ const simpleHash = (str: string): string => {
13
13
  const normalizeProps = <P extends AnyRecord>(props: P, keys: Array<keyof P>): Partial<P> => {
14
14
  return keys.reduce((acc, key) => {
15
15
  if (key === 'children') {
16
- acc[key] = Children.map(props[key], (child) =>
17
- typeof child === 'object' ? '[Component]' : child
16
+ acc[key] = Children.map(props[key], (child) => typeof child === 'object' ? '[Component]' : child,
18
17
  )
19
18
  } else {
20
19
  acc[key] = props[key]
@@ -28,17 +27,25 @@ const normalizeDebugName = (debugName: string) => {
28
27
  }
29
28
 
30
29
  const generateComponentTestId = <P extends AnyRecord>(componentName: string, props: P, keys: Array<keyof P>) => {
31
- const hasDebugName = typeof props?.['debugName'] === 'string'
32
- if (hasDebugName) return `${componentName}:${normalizeDebugName(props?.['debugName'])}`
30
+ const hasDebugName = typeof props?.debugName === 'string'
31
+ if (hasDebugName) return `${componentName}:${normalizeDebugName(props?.debugName)}`
33
32
  const extractedProps = normalizeProps(props, keys)
34
33
  return `${componentName}:${simpleHash(JSON.stringify(extractedProps))}`
35
34
  }
36
35
 
36
+ /**
37
+ * Hook that generates a stable test ID for a component based on its props.
38
+ * Uses debugName if available, otherwise generates a hash from specified prop keys.
39
+ *
40
+ * @example
41
+ * const testId = useComponentTestId(Button, props, ['label', 'variant'])
42
+ * // Returns: "Button:debug-name" or "Button:123456"
43
+ */
37
44
  export const useComponentTestId = <P extends AnyRecord>(
38
45
  Component: any,
39
46
  props: P,
40
- keys: Array<keyof P>
47
+ keys: Array<keyof P>,
41
48
  ) => {
42
49
  const testIdRef = useRef(generateComponentTestId(Component.styleRegistryName, props, keys))
43
50
  return testIdRef.current
44
- }
51
+ }
@@ -9,10 +9,21 @@ type UseConditionalStateOptions<T> = {
9
9
 
10
10
  type SetState<T> = Dispatch<SetStateAction<T>> & ((value: T) => void) & (() => void)
11
11
 
12
+ /**
13
+ * Hook that uses external state if provided, otherwise creates internal state.
14
+ * Useful for creating controlled/uncontrolled component patterns.
15
+ *
16
+ * @example
17
+ * // Controlled mode
18
+ * const [value, setValue] = useConditionalState(props.value, props.onChange)
19
+ *
20
+ * // Uncontrolled mode
21
+ * const [value, setValue] = useConditionalState(undefined, undefined, { initialValue: 'default' })
22
+ */
12
23
  export const useConditionalState = <T>(
13
24
  value: T | undefined,
14
25
  setter: AnyFunction,
15
- options: UseConditionalStateOptions<T> = {}
26
+ options: UseConditionalStateOptions<T> = {},
16
27
  ): [T, SetState<T>] => {
17
28
  const state = options?.isBooleanToggle
18
29
  ? useBooleanToggle(options?.initialValue as boolean)
@@ -1,5 +1,12 @@
1
1
  import { useEffect, useRef, useState } from 'react'
2
2
 
3
+ /**
4
+ * Hook that debounces a value, updating it after a specified delay.
5
+ *
6
+ * @example
7
+ * const [debouncedSearch, resetDebounce] = useDebounce(searchTerm, 500)
8
+ * // debouncedSearch updates 500ms after searchTerm stops changing
9
+ */
3
10
  export function useDebounce<T>(
4
11
  value: T,
5
12
  debounce: number,
@@ -0,0 +1,47 @@
1
+ import { useRef, useCallback } from 'react'
2
+
3
+ /**
4
+ * Hook that creates debounced, flush, and cancel functions for a callback.
5
+ *
6
+ * @example
7
+ * const { debounce, flush, cancel } = useDebounceCallback((value) => {
8
+ * console.log(value)
9
+ * }, 500)
10
+ *
11
+ * debounce('hello') // Will call callback after 500ms
12
+ * flush('immediate') // Calls callback immediately
13
+ * cancel() // Cancels pending debounced call
14
+ */
15
+ export function useDebounceCallback<T extends any[]>(
16
+ callback: (...args: T) => void,
17
+ delay = 1000,
18
+ ) {
19
+ const timeoutRef = useRef(null)
20
+
21
+ const debounce = useCallback((...args: T) => {
22
+ cancel()
23
+
24
+ timeoutRef.current = setTimeout(() => {
25
+ callback(...args)
26
+ timeoutRef.current = null
27
+ }, delay)
28
+ }, [callback])
29
+
30
+ const flush = useCallback((...args: T) => {
31
+ cancel()
32
+ callback(...args)
33
+ }, [callback])
34
+
35
+ const cancel = useCallback(() => {
36
+ if (timeoutRef.current) {
37
+ clearTimeout(timeoutRef.current)
38
+ timeoutRef.current = null
39
+ }
40
+ }, [])
41
+
42
+ return {
43
+ debounce,
44
+ flush,
45
+ cancel,
46
+ }
47
+ }
@@ -0,0 +1,21 @@
1
+ import { useEffect, useRef } from 'react'
2
+
3
+ /**
4
+ * Hook that creates a ref that automatically updates when a derived value changes.
5
+ *
6
+ * @example
7
+ * const userNameRef = useDerivedRef(user, (u) => u.name)
8
+ * // userNameRef.current will always contain the latest user name
9
+ */
10
+ export const useDerivedRef = <T, D>(
11
+ derivedValue: D,
12
+ getValue: (derivedValue: D) => T,
13
+ ): React.MutableRefObject<T> => {
14
+ const ref = useRef(getValue(derivedValue))
15
+
16
+ useEffect(() => {
17
+ ref.current = getValue(derivedValue)
18
+ }, [derivedValue])
19
+
20
+ return ref
21
+ }
@@ -0,0 +1,42 @@
1
+ import { deepEqual } from '@codeleap/utils'
2
+ import { useState, useEffect } from 'react'
3
+
4
+ type Options<T, D> = {
5
+ areEqual?: (currentState: T, derivedValue: D) => boolean
6
+ transform?: (value: D | T) => any
7
+ getValue?: (derivedValue: D) => T
8
+ }
9
+
10
+ /**
11
+ * Hook that creates a state that synchronizes with a derived value, with customizable equality check.
12
+ *
13
+ * @example
14
+ * const [state, setState] = useDerivedState(props.value, {
15
+ * getValue: (v) => v.id,
16
+ * areEqual: (a, b) => a === b.id
17
+ * })
18
+ */
19
+ export const useDerivedState = <T, D = T>(
20
+ derivedValue: D,
21
+ options: Options<T, D> = {},
22
+ ): [T, React.Dispatch<React.SetStateAction<T>>] => {
23
+ const {
24
+ getValue = (value: any) => value as T,
25
+ transform = (value) => value,
26
+ areEqual = (currentState, derivedValue) => deepEqual(currentState, derivedValue),
27
+ } = options
28
+
29
+ const [state, setState] = useState<T>(() => getValue(derivedValue))
30
+
31
+ useEffect(() => {
32
+ const newValue = getValue(derivedValue)
33
+
34
+ const stateAreEqual = areEqual(transform(state), transform(derivedValue))
35
+
36
+ if (!stateAreEqual) {
37
+ setState(newValue)
38
+ }
39
+ }, [derivedValue])
40
+
41
+ return [state, setState]
42
+ }
@@ -1,5 +1,14 @@
1
1
  import { EffectCallback, useEffect } from 'react'
2
2
 
3
+ /**
4
+ * Hook that runs an effect only once when the component mounts.
5
+ *
6
+ * @example
7
+ * useEffectOnce(() => {
8
+ * console.log('Runs only once on mount')
9
+ * return () => console.log('Cleanup on unmount')
10
+ * })
11
+ */
3
12
  export const useEffectOnce = (effect: EffectCallback) => {
4
13
  useEffect(effect, [])
5
14
  }
@@ -0,0 +1,21 @@
1
+ import { useMemo } from 'react'
2
+
3
+ /**
4
+ * Hook that filters out the first item from a list that matches a predicate.
5
+ *
6
+ * @example
7
+ * const filteredUsers = useFilteredList(users, (user) => user.id === deletedId)
8
+ * // Returns users array without the first user matching the predicate
9
+ */
10
+ export function useFilteredList<T>(list: T[], predicate: (item: T) => boolean): T[] {
11
+ return useMemo(() => {
12
+ if (!list) return []
13
+
14
+ const index = list.findIndex(predicate)
15
+ if (index === -1) return list
16
+
17
+ const newList = [...list]
18
+ newList.splice(index, 1)
19
+ return newList
20
+ }, [list, predicate])
21
+ }
@@ -1,5 +1,12 @@
1
1
  import { useReducer } from 'react'
2
2
 
3
+ /**
4
+ * Hook that returns a function to force a component re-render.
5
+ *
6
+ * @example
7
+ * const forceRender = useForceRender()
8
+ * forceRender() // Forces component to re-render
9
+ */
3
10
  export function useForceRender() {
4
11
  const [_, forceRender] = useReducer((x) => x + 1, 0)
5
12
  return forceRender
package/src/useId.ts CHANGED
@@ -1,8 +1,15 @@
1
1
  import { useRef, useId as _useId } from 'react'
2
2
 
3
+ /**
4
+ * Hook that returns a stable ID, using provided ID if available or generating one.
5
+ *
6
+ * @example
7
+ * const id = useId('custom-id') // Returns 'custom-id'
8
+ * const id = useId() // Returns generated ID like ':r1:'
9
+ */
3
10
  export function useId<T>(id?: T) {
4
11
  const defaultId = _useId()
5
12
  const idRef = useRef(id)
6
13
 
7
14
  return idRef.current ?? defaultId
8
- }
15
+ }
@@ -3,6 +3,19 @@ import { AnyFunction } from '@codeleap/types'
3
3
 
4
4
  type UseIntervalHandler = (clear: AnyFunction) => void
5
5
 
6
+ /**
7
+ * Hook that manages an interval with start and clear controls.
8
+ * The handler receives a clear function to stop the interval from within.
9
+ *
10
+ * @example
11
+ * const { start, clear } = useInterval((clearFn) => {
12
+ * console.log('Tick')
13
+ * if (shouldStop) clearFn()
14
+ * }, 1000)
15
+ *
16
+ * start() // Starts the interval
17
+ * clear() // Stops the interval
18
+ */
6
19
  export function useInterval(handler: UseIntervalHandler, interval: number) {
7
20
  const intervalRef = useRef<NodeJS.Timer | null>(null)
8
21
  const handlerRef = useRef<UseIntervalHandler>(handler)
@@ -7,7 +7,12 @@ const useIsomorphicLayoutEffect =
7
7
 
8
8
  /**
9
9
  * Hook to check if the component has mounted. SSR safe.
10
- * @return true after the component's html is mounted in the browser.
10
+ *
11
+ * @example
12
+ * const isMounted = useIsMounted()
13
+ * if (isMounted) {
14
+ * // Safe to use browser APIs
15
+ * }
11
16
  */
12
17
  export function useIsMounted() {
13
18
  const [mounted, setMounted] = useState(false)
@@ -22,7 +27,12 @@ export function useIsMounted() {
22
27
  /**
23
28
  * Hook to check if the code is running on the client-side.
24
29
  * NOTE: This is just a mirror of `useIsMounted` for retrocompatibility reasons
25
- * @return `isClient: true` after the component is mounted in the browser.
30
+ *
31
+ * @example
32
+ * const { isClient } = useIsClient()
33
+ * if (isClient) {
34
+ * // Safe to use browser APIs
35
+ * }
26
36
  */
27
37
  export function useIsClient() {
28
38
  return {
@@ -0,0 +1,18 @@
1
+ import { globalState } from '@codeleap/store'
2
+ import { useMemo } from 'react'
3
+
4
+ /**
5
+ * Hook that lazily creates a global store with an initial value.
6
+ * The store is created only once and persists across re-renders.
7
+ *
8
+ * @example
9
+ * const counterStore = useLazyStore(0)
10
+ * // Store is created once and can be used across components
11
+ */
12
+ export function useLazyStore<T>(initialValue: T) {
13
+ const store = useMemo(() => {
14
+ return globalState(initialValue)
15
+ }, [])
16
+
17
+ return store
18
+ }
package/src/useModal.ts CHANGED
@@ -1,6 +1,16 @@
1
1
  import { TypeGuards } from '@codeleap/types'
2
2
  import { useState } from 'react'
3
3
 
4
+ /**
5
+ * Hook that manages modal visibility state with open, close, and toggle functions.
6
+ *
7
+ * @example
8
+ * const modal = useModal()
9
+ * modal.open() // Opens modal
10
+ * modal.close() // Closes modal
11
+ * modal.toggle() // Toggles visibility
12
+ * modal.toggle(true) // Forces open
13
+ */
4
14
  export function useModal(startsOpen = false) {
5
15
  const [visible, setVisible] = useState(startsOpen)
6
16
 
@@ -17,7 +27,7 @@ export function useModal(startsOpen = false) {
17
27
  }
18
28
 
19
29
  return {
20
- visible,
30
+ visible,
21
31
  toggle,
22
32
  open,
23
33
  close,
@@ -0,0 +1,27 @@
1
+ import { useMemo, useState } from 'react'
2
+
3
+ /**
4
+ * Hook that manages selected option state with boolean flags for each option.
5
+ *
6
+ * @example
7
+ * const { selectedOption, setSelectedOption, isSelected } = useOptions(['light', 'dark'] as const)
8
+ * // isSelected = { light: true, dark: false }
9
+ * setSelectedOption('dark')
10
+ * // isSelected = { light: false, dark: true }
11
+ */
12
+ export function useOptions<T extends string>(options: readonly T[], initialOptions: T = options[0]) {
13
+ const [selectedOption, setSelectedOption] = useState<T>(initialOptions)
14
+
15
+ const isSelected = useMemo(() => {
16
+ return options.reduce((acc, option) => {
17
+ acc[option] = option === selectedOption
18
+ return acc
19
+ }, {} as Record<T, boolean>)
20
+ }, [selectedOption, options])
21
+
22
+ return {
23
+ selectedOption,
24
+ setSelectedOption,
25
+ isSelected,
26
+ }
27
+ }
@@ -4,6 +4,14 @@ import { deepMerge } from '@codeleap/utils'
4
4
 
5
5
  type SetPartialStateCallback<T> = (value: T) => DeepPartial<T>
6
6
 
7
+ /**
8
+ * Hook that manages state with partial updates using deep merge.
9
+ *
10
+ * @example
11
+ * const [user, setUser] = usePartialState({ name: 'John', age: 30 })
12
+ * setUser({ age: 31 }) // Only updates age, keeps name
13
+ * setUser(prev => ({ age: prev.age + 1 })) // Functional update
14
+ */
7
15
  export function usePartialState<T= any>(initial: T | (() => T)) {
8
16
  const [state, setState] = useState(initial)
9
17
 
package/src/usePlaces.ts CHANGED
@@ -13,6 +13,9 @@ type Params = {
13
13
  showDetails?: boolean
14
14
  }
15
15
 
16
+ /**
17
+ * Retrieves detailed information for a specific Google Place.
18
+ */
16
19
  export const retrievePlaceDetails = async (placeId: string, apiKey: string) => {
17
20
  const response = await axios.get(BASE_URL_DETAILS, {
18
21
  params: {
@@ -24,6 +27,10 @@ export const retrievePlaceDetails = async (placeId: string, apiKey: string) => {
24
27
  return response?.data?.result
25
28
  }
26
29
 
30
+ /**
31
+ * Retrieves places from Google Places API or Geocoding API.
32
+ * Supports both text search and lat/lng coordinates.
33
+ */
27
34
  export const retrievePlaces = async (params: Params) => {
28
35
  let response
29
36
  const inputWithoutSpaces = params?.input?.replace(/\s/g, '')
@@ -53,6 +60,17 @@ export const retrievePlaces = async (params: Params) => {
53
60
 
54
61
  export type UsePlacesParams = Params
55
62
 
63
+ /**
64
+ * Hook that fetches Google Places using React Query.
65
+ * Automatically handles caching and refetching.
66
+ *
67
+ * @example
68
+ * const places = usePlaces({
69
+ * input: 'New York',
70
+ * key: 'YOUR_API_KEY',
71
+ * showDetails: true
72
+ * })
73
+ */
56
74
  export const usePlaces = (params: Params) => {
57
75
  const places = useQuery({
58
76
  queryKey: ['places', params],
@@ -7,6 +7,22 @@ export type UsePlacesAutocompleteUtilsProps<T extends Record<string, any>> = {
7
7
  onPress?: (address: string, item: T) => void
8
8
  }
9
9
 
10
+ /**
11
+ * Hook that manages address autocomplete state with debounced input handling.
12
+ * Useful for Google Places autocomplete implementations.
13
+ *
14
+ * @example
15
+ * const {
16
+ * address,
17
+ * handleChangeAddress,
18
+ * handlePressAddress,
19
+ * isTyping
20
+ * } = usePlacesAutocompleteUtils({
21
+ * debounce: 500,
22
+ * onValueChange: (addr) => fetchPlaces(addr),
23
+ * onPress: (addr, item) => console.log('Selected:', item)
24
+ * })
25
+ */
10
26
  export const usePlacesAutocompleteUtils = <T extends Record<string, any>>(props: UsePlacesAutocompleteUtilsProps<T>) => {
11
27
  const {
12
28
  debounce = 250,
@@ -1,5 +1,15 @@
1
1
  import { useEffect, useRef } from 'react'
2
2
 
3
+ /**
4
+ * Hook that returns the previous value of a variable.
5
+ * The value is updated after render is committed to the DOM.
6
+ *
7
+ * @example
8
+ * const [count, setCount] = useState(0)
9
+ * const prevCount = usePrevious(count)
10
+ * // On first render: count=0, prevCount=null
11
+ * // After setCount(1): count=1, prevCount=0
12
+ */
3
13
  export const usePrevious = <T>(value: T) => {
4
14
  const ref = useRef<T>(null)
5
15
  useEffect(() => {
package/src/usePromise.ts CHANGED
@@ -8,10 +8,24 @@ type UsePromiseOptions<T = any> = {
8
8
  debugName?: string
9
9
  }
10
10
 
11
+ /**
12
+ * Hook that creates a deferred promise with manual resolve/reject control.
13
+ * Useful for coordinating asynchronous operations across component lifecycle.
14
+ *
15
+ * @example
16
+ * const promise = usePromise({ timeout: 5000 })
17
+ * const handleClick = async () => {
18
+ * const result = await promise._await()
19
+ * console.log(result)
20
+ * }
21
+ * // Later, from another callback:
22
+ * promise.resolve('success')
23
+ */
11
24
  export const usePromise = <T = any>(options?: UsePromiseOptions<T>) => {
12
25
  const rejectRef = useRef<AnyFunction>(null)
13
26
  const resolveRef = useRef<(v:T) => any>(null)
14
27
  const timeoutRef = useRef(null)
28
+
15
29
  const reject = async (err: any) => {
16
30
  await rejectRef.current?.(err)
17
31
  options?.onReject?.(err)
@@ -3,6 +3,22 @@ import { useBooleanToggle } from '../useBooleanToggle'
3
3
  import { useSearchParams } from './types'
4
4
  import { TypeGuards } from '@codeleap/types'
5
5
 
6
+ /**
7
+ * Hook that manages searchable select/autocomplete state with async loading support.
8
+ * Handles both local filtering and remote data fetching.
9
+ *
10
+ * @example
11
+ * const search = useSearch({
12
+ * value: 'selected-value',
13
+ * multiple: false,
14
+ * defaultOptions: [...],
15
+ * loadOptions: async (query) => fetchOptions(query),
16
+ * filterItems: (query, opts) => opts.filter(o => o.label.includes(query))
17
+ * })
18
+ *
19
+ * search.onChangeSearch('query')
20
+ * search.load()
21
+ */
6
22
  export function useSearch<T extends string|number = string, Multi extends boolean = false>(props: useSearchParams<T, Multi>) {
7
23
  const {
8
24
  value,
package/src/useToggle.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import { useState } from 'react'
2
2
 
3
+ /**
4
+ * Hook that toggles between two values.
5
+ *
6
+ * @example
7
+ * const [theme, toggleTheme] = useToggle(['light', 'dark'] as const, 'light')
8
+ * toggleTheme() // Switches to 'dark'
9
+ * toggleTheme('light') // Sets to 'light'
10
+ */
3
11
  export function useToggle<T extends readonly [any, any], V extends T[0] | T[1]>(
4
12
  options: T,
5
13
  initial: V,
@@ -11,6 +11,18 @@ export interface UncontrolledOptions<T> {
11
11
  rule: (value: T | null | undefined) => boolean
12
12
  }
13
13
 
14
+ /**
15
+ * Hook that manages controlled/uncontrolled component pattern with smooth transitions.
16
+ *
17
+ * @example
18
+ * const [value, handleChange, mode] = useUncontrolled({
19
+ * value: props.value,
20
+ * defaultValue: 'default',
21
+ * finalValue: '',
22
+ * rule: (v) => v !== undefined,
23
+ * onChange: (v) => props.onChange?.(v)
24
+ * })
25
+ */
14
26
  export function useUncontrolled<T>({
15
27
  value,
16
28
  defaultValue,
package/src/useUnmount.ts CHANGED
@@ -1,6 +1,16 @@
1
1
  import { useRef } from 'react'
2
2
  import { useEffectOnce } from './useEffectOnce'
3
3
 
4
+ /**
5
+ * Hook that runs a cleanup function when the component unmounts.
6
+ * The function reference is updated on each render to always use the latest version.
7
+ *
8
+ * @example
9
+ * useUnmount(() => {
10
+ * console.log('Component unmounting')
11
+ * // Cleanup logic here
12
+ * })
13
+ */
4
14
  export const useUnmount = (fn: () => any): void => {
5
15
  const fnRef = useRef(fn)
6
16
 
package/src/useCounter.ts DELETED
@@ -1,5 +0,0 @@
1
- import { useReducer } from 'react'
2
-
3
- export function useCounter() {
4
- return useReducer((x) => x + 1, 0)
5
- }
@@ -1,92 +0,0 @@
1
- import { useState } from 'react'
2
-
3
- export interface UseListStateHandler<T> {
4
- setState: React.Dispatch<React.SetStateAction<T[]>>
5
- append: (...items: T[]) => void
6
- prepend: (...items: T[]) => void
7
- insert: (index: number, ...items: T[]) => void
8
- pop: () => void
9
- shift: () => void
10
- apply: (fn: (item: T, index?: number) => T) => void
11
- applyWhere: (
12
- condition: (item: T, index: number) => boolean,
13
- fn: (item: T, index?: number) => T
14
- ) => void
15
- remove: (...indices: number[]) => void
16
- reorder: ({ from, to }: { from: number; to: number }) => void
17
- setItem: (index: number, item: T) => void
18
- setItemProp: <K extends keyof T, U extends T[K]>(index: number, prop: K, value: U) => void
19
- }
20
-
21
- export type UseListState<T> = [T[], UseListStateHandler<T>]
22
-
23
- export function useListState<T>(initialValue: (T[] | (() => T[])) = []): UseListState<T> {
24
- const [state, setState] = useState(initialValue)
25
-
26
- const append = (...items: T[]) => setState((current) => [...current, ...items])
27
- const prepend = (...items: T[]) => setState((current) => [...items, ...current])
28
-
29
- const insert = (index: number, ...items: T[]) => setState((current) => [...current.slice(0, index), ...items, ...current.slice(index)])
30
-
31
- const apply = (fn: (item: T, index?: number) => T) => setState((current) => current.map((item, index) => fn(item, index)))
32
-
33
- const remove = (...indices: number[]) => setState((current) => current.filter((_, index) => !indices.includes(index)))
34
-
35
- const pop = () => setState((current) => {
36
- const cloned = [...current]
37
- cloned.pop()
38
- return cloned
39
- })
40
-
41
- const shift = () => setState((current) => {
42
- const cloned = [...current]
43
- cloned.shift()
44
- return cloned
45
- })
46
-
47
- const reorder = ({ from, to }: { from: number; to: number }) => setState((current) => {
48
- const cloned = [...current]
49
- const item = current[from]
50
-
51
- cloned.splice(from, 1)
52
- cloned.splice(to, 0, item)
53
-
54
- return cloned
55
- })
56
-
57
- const setItem = (index: number, item: T) => setState((current) => {
58
- const cloned = [...current]
59
- cloned[index] = item
60
- return cloned
61
- })
62
-
63
- const setItemProp = <K extends keyof T, U extends T[K]>(index: number, prop: K, value: U) => setState((current) => {
64
- const cloned = [...current]
65
- cloned[index] = { ...cloned[index], [prop]: value }
66
- return cloned
67
- })
68
-
69
- const applyWhere = (
70
- condition: (item: T, index: number) => boolean,
71
- fn: (item: T, index?: number) => T,
72
- ) => setState((current) => current.map((item, index) => (condition(item, index) ? fn(item, index) : item)),
73
- )
74
-
75
- return [
76
- state,
77
- {
78
- setState,
79
- append,
80
- prepend,
81
- insert,
82
- pop,
83
- shift,
84
- apply,
85
- applyWhere,
86
- remove,
87
- reorder,
88
- setItem,
89
- setItemProp,
90
- },
91
- ]
92
- }