@hookform/lenses
Version:
Type-safe lenses for React Hook Form that enable precise control over nested form state. Build reusable form components with composable operations, array handling, and full TypeScript support.
1 lines • 11.5 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/LensCore.ts","../src/LensesStorage.ts","../src/useLens.ts"],"sourcesContent":["export * from './LensCore';\nexport * from './LensesStorage';\nexport * from './types/helpers';\nexport * from './types/lenses';\nexport * from './useLens';\n","import { type Control, type FieldValues, get } from 'react-hook-form';\n\nimport type { LensesValues } from './types/helpers';\nimport type { Lens } from './types/lenses';\nimport type { LensesStorage } from './LensesStorage';\n\ninterface Settings {\n lensesMap?: Record<string, LensCore> | [Record<string, LensCore>] | undefined;\n propPath?: string | undefined;\n restructureSourcePath?: string | undefined;\n}\n\nexport class LensCore {\n public control: Control;\n public settings: Settings;\n public cache?: LensesStorage | undefined;\n\n private constructor(control: Control<any>, cache?: LensesStorage, settings: Settings = {}) {\n this.control = control;\n this.settings = settings;\n this.cache = cache;\n }\n\n public static create<TFieldValues extends FieldValues = FieldValues>(\n control: Control<TFieldValues>,\n cache?: LensesStorage,\n ): Lens<TFieldValues> {\n return new LensCore(control, cache) as unknown as Lens<TFieldValues>;\n }\n\n public focus(propPath: string): LensCore {\n let nestedPath = this.settings.propPath === undefined ? propPath : `${this.settings.propPath}.${propPath}`;\n\n if (this.settings.lensesMap) {\n if (Array.isArray(this.settings.lensesMap)) {\n const arrayReflectMapper: LensCore | undefined = get(this.settings.lensesMap[0], propPath);\n\n if (arrayReflectMapper) {\n const reflectedPropPath = arrayReflectMapper.settings.propPath?.slice(`${this.settings.restructureSourcePath}.`.length);\n\n if (reflectedPropPath) {\n nestedPath = `${this.settings.propPath}.${reflectedPropPath}`;\n\n if (!this.cache?.has(nestedPath)) {\n const newLens = new LensCore(arrayReflectMapper.control, arrayReflectMapper.cache, {\n ...arrayReflectMapper.settings,\n propPath: nestedPath,\n });\n this.cache?.set(newLens, nestedPath);\n }\n\n const focusedLens = this.cache?.get(nestedPath);\n\n if (!focusedLens) {\n throw new Error(`There is no focused lens: ${nestedPath}`);\n }\n\n return focusedLens;\n }\n }\n } else {\n const result = get(this.settings.lensesMap, propPath);\n if (result) {\n return result;\n }\n }\n }\n\n if (!this.cache?.has(nestedPath)) {\n const newLens = new LensCore(this.control, this.cache, {\n propPath: nestedPath,\n lensesMap: this.settings.lensesMap,\n restructureSourcePath: this.settings.restructureSourcePath,\n });\n this.cache?.set(newLens, nestedPath);\n }\n\n const focusedLens = this.cache?.get(nestedPath);\n\n if (!focusedLens) {\n throw new Error(`There is no focused lens: ${nestedPath}`);\n }\n\n return focusedLens;\n }\n\n public reflect(getter: (value: LensesValues<any>, lens: LensCore) => Record<string, LensCore> | [Record<string, LensCore>]): LensCore {\n const fromCache = this.cache?.get(this.settings.propPath ?? '', getter);\n\n if (fromCache) {\n return fromCache;\n }\n\n const proxy = new Proxy(\n {},\n {\n get: (target, prop) => {\n if (typeof prop === 'string') {\n return this.focus(prop);\n }\n\n return target;\n },\n },\n );\n\n const focusContext = getter(proxy, this);\n\n const newLens = new LensCore(this.control, this.cache, {\n lensesMap: focusContext,\n propPath: this.settings.propPath,\n restructureSourcePath: this.settings.propPath,\n });\n\n this.cache?.set(newLens, this.settings.propPath ?? '', getter);\n\n return newLens;\n }\n\n public map<R>(\n fields: Record<string, any>[],\n mapper: (value: unknown, item: LensCore, index: number, array: unknown[], lens: this) => R,\n ): R[] {\n if (!this.settings.propPath) {\n throw new Error(`There is no prop name in this lens: ${this.settings.propPath}`);\n }\n\n return fields.map((value, index, array) => {\n const item = this.focus(index.toString());\n const res = mapper(value, item, index, array, this);\n return res;\n });\n }\n\n public interop(cb?: (control: Control, name: string | undefined, lens: LensCore) => any): {\n control: Control;\n name: string | undefined;\n lens: LensCore;\n } {\n return cb ? cb(this.control, this.settings.propPath, this) : { control: this.control, name: this.settings.propPath, lens: this };\n }\n}\n","import type { Control, FieldValues } from 'react-hook-form';\n\nimport type { LensCore } from './LensCore';\n\nexport type LensesStorageComplexKey = (...args: any[]) => any;\n\nexport interface LensesStorageValue {\n plain?: LensCore;\n complex: WeakMap<LensesStorageComplexKey, LensCore>;\n}\n\nexport type LensCache = Map<string, LensesStorageValue>;\n\nexport class LensesStorage<TFieldValues extends FieldValues = FieldValues> {\n private cache: LensCache;\n\n constructor(control: Control<TFieldValues>) {\n this.cache = new Map();\n\n control?._subjects?.values?.subscribe?.({\n next: () => {\n control._names.unMount.forEach((name) => {\n this.delete(name);\n });\n },\n });\n }\n\n public get(propPath: string, complexKey?: LensesStorageComplexKey): LensCore | undefined {\n const cached = this.cache.get(propPath);\n\n if (cached) {\n if (complexKey) {\n return cached.complex.get(complexKey);\n }\n\n return cached.plain;\n }\n\n return undefined;\n }\n\n public set(lens: LensCore, propPath: string, complexKey?: LensesStorageComplexKey): void {\n let cached = this.cache.get(propPath);\n\n if (!cached) {\n cached = {\n complex: new WeakMap(),\n };\n\n this.cache.set(propPath, cached);\n }\n\n if (complexKey) {\n cached.complex.set(complexKey, lens);\n } else {\n cached.plain = lens;\n }\n }\n\n public has(propPath: string, complexKey?: LensesStorageComplexKey): boolean {\n if (complexKey) {\n return this.cache.get(propPath)?.complex.has(complexKey) ?? false;\n }\n\n return this.cache.has(propPath);\n }\n\n public delete(propPath: string): void {\n for (const key of this.cache.keys()) {\n if (key.startsWith(propPath)) {\n this.cache.delete(key);\n }\n }\n }\n\n public clear(): void {\n this.cache.clear();\n }\n}\n","import { type DependencyList, useMemo } from 'react';\nimport type { Control, FieldValues } from 'react-hook-form';\n\nimport type { Lens } from './types/lenses';\nimport { LensCore } from './LensCore';\nimport { LensesStorage } from './LensesStorage';\n\nexport interface UseLensProps<TFieldValues extends FieldValues = FieldValues> {\n control: Control<TFieldValues>;\n}\n\n/**\n * Creates lens from react-hook-form control.\n *\n * @example\n * ```tsx\n * function App() {\n * const { control } = useForm<{\n * firstName: string;\n * }>();\n *\n * const lens = useLens({ control });\n * }\n * ```\n */\nexport function useLens<TFieldValues extends FieldValues = FieldValues>(\n props: UseLensProps<TFieldValues>,\n deps: DependencyList = [],\n): Lens<TFieldValues> {\n return useMemo(() => {\n const cache = new LensesStorage(props.control);\n const lens = LensCore.create<TFieldValues>(props.control, cache);\n\n return lens;\n }, [props.control, ...deps]);\n}\n"],"mappings":"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","names":["focusedLens"]}