|
| 1 | +"use client"; |
| 2 | + |
| 3 | +import { ComponentData, Config } from "../../types"; |
| 4 | +import { useMemo, useRef } from "react"; |
| 5 | +import { mapFields, Mappers } from "../data/map-fields"; |
| 6 | +import { FieldTransforms } from "../../types/API/FieldTransforms"; |
| 7 | +import { buildMappers } from "./build-mappers"; |
| 8 | + |
| 9 | +export function useFieldTransformsTracked< |
| 10 | + T extends ComponentData, |
| 11 | + UserConfig extends Config |
| 12 | +>( |
| 13 | + config: UserConfig, |
| 14 | + item: T, |
| 15 | + transforms: FieldTransforms, |
| 16 | + readOnly?: T["readOnly"], |
| 17 | + forceReadOnly?: boolean |
| 18 | +): T["props"] { |
| 19 | + const prevProps = useRef<Record<string, any>>(null); |
| 20 | + const prevResult = useRef<Record<string, any>>(item.props); |
| 21 | + |
| 22 | + const mappers = useMemo<Mappers>( |
| 23 | + () => buildMappers(transforms, readOnly, forceReadOnly), |
| 24 | + [transforms, readOnly, forceReadOnly] |
| 25 | + ); |
| 26 | + |
| 27 | + const transformedProps = useMemo(() => { |
| 28 | + // Filter to changed fields only (shallow comparison) |
| 29 | + const changedProps: Record<string, any> = {}; |
| 30 | + |
| 31 | + const componentConfig = |
| 32 | + item.type === "root" ? config.root : config.components?.[item.type]; |
| 33 | + |
| 34 | + let changeIncludesSlot = false; |
| 35 | + |
| 36 | + for (const fieldName in item.props) { |
| 37 | + const fieldType = componentConfig?.fields?.[fieldName]?.type; |
| 38 | + |
| 39 | + if ( |
| 40 | + !prevProps.current || |
| 41 | + item.props[fieldName] !== prevProps.current[fieldName] |
| 42 | + ) { |
| 43 | + changedProps[fieldName] = item.props[fieldName]; |
| 44 | + |
| 45 | + if (fieldType === "slot") { |
| 46 | + changeIncludesSlot = true; |
| 47 | + } |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + // Always include ID |
| 52 | + changedProps.id = item.props.id; |
| 53 | + |
| 54 | + prevProps.current = item.props; |
| 55 | + |
| 56 | + const mapped = mapFields( |
| 57 | + { ...item, props: changedProps }, |
| 58 | + mappers, |
| 59 | + config, |
| 60 | + false, |
| 61 | + changeIncludesSlot |
| 62 | + ).props; |
| 63 | + |
| 64 | + prevResult.current = { ...prevResult.current, ...mapped }; |
| 65 | + |
| 66 | + return prevResult.current; |
| 67 | + }, [config, item, mappers]); |
| 68 | + |
| 69 | + const mergedProps = useMemo( |
| 70 | + () => ({ ...item.props, ...transformedProps }), |
| 71 | + [item.props, transformedProps] |
| 72 | + ); |
| 73 | + |
| 74 | + return mergedProps; |
| 75 | +} |
0 commit comments