UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

106 lines (92 loc) 2.69 kB
import {type Path, type PathSegment} from '@sanity/types' import {find, get} from 'lodash' import {useCallback} from 'react' import shallowEquals from 'shallow-equals' import {isRecord} from '../../../util' import {type FormPatch} from '../../patch' import {useFormBuilder} from '../../useFormBuilder' /** * @internal */ export interface PatchesData { patches: Array<FormPatch> shouldReset: boolean snapshot: any } /** * @internal */ export type PatchesSubscriber = (data: PatchesData) => void /** * @internal */ export function usePatches(props: {path: Path}): { subscribe: (subscriber: PatchesSubscriber) => () => void } { const {path} = props const {patchChannel} = useFormBuilder().__internal const subscribe = useCallback( (subscriber: PatchesSubscriber) => { return patchChannel.subscribe(({snapshot, patches}) => { const filteredPatches = patches .filter((patch) => _startsWith(patch.path, path)) .map((patch) => ({ ...patch, path: patch.path.slice(path.length), })) if (filteredPatches.length) { subscriber({ shouldReset: _shouldReset(path, patches), snapshot: isRecord(snapshot) ? _getValueAtPath(snapshot, path) : {}, patches: filteredPatches, }) } }) }, [path, patchChannel], ) return {subscribe} } function _isSegmentEqual(segment1: PathSegment, segment2: PathSegment) { const segment1Type = typeof segment1 if (segment1Type !== typeof segment2) { return false } if (segment1Type === 'object') { return shallowEquals(segment1, segment2) } return segment1 === segment2 } function _startsWith(subjectPath: Path, checkPath: Path) { if (subjectPath === checkPath) { return true } if (!Array.isArray(subjectPath) || !Array.isArray(checkPath)) { return false } if (subjectPath.length < checkPath.length) { return false } for (let i = 0, len = checkPath.length; i < len; i++) { if (!_isSegmentEqual(checkPath[i], subjectPath[i])) { return false } } return true } function _isAncestor(path1: Path, path2: Path) { return path1.length === 0 || (_startsWith(path2, path1) && !_startsWith(path1, path2)) } function _shouldReset(path: Path, patches: FormPatch[]) { return patches.some( (patch) => _isAncestor(patch.path, path) && (patch.type === 'set' || patch.type === 'unset'), ) } function _getValueAtPath(value: Record<string, unknown>, path: Path) { return path.reduce((result, segment) => { if (typeof segment === 'object') { return find(result, segment) } return get(result, segment) }, value) }