@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
119 lines (118 loc) • 4.34 kB
JavaScript
"use client";
import { useCallback, useContext, useMemo, useRef, useSyncExternalStore } from 'react';
import pointer, { isPath } from "../utils/json-pointer/index.js";
import DataContextRefContext from "../DataContext/DataContextRefContext.js";
import IterateItemContext from "../Iterate/IterateItemContext.js";
import usePath from "./usePath.js";
export default function useDataValue(pathProp, value, options = {}) {
const providedDataContextRef = useContext(DataContextRefContext);
const fallbackDataContextRef = useRef(undefined);
const dataContextRef = providedDataContextRef !== null && providedDataContextRef !== void 0 ? providedDataContextRef : fallbackDataContextRef;
const iterateItemContext = useContext(IterateItemContext);
const snapshotVersionRef = useRef(0);
const {
pathType = 'auto'
} = options;
const {
makePath,
makeIteratePath
} = usePath();
const resolvePath = useCallback(path => {
if (pathType === 'absolute') {
return makePath(path);
}
if (pathType === 'iterate') {
return makeIteratePath(path);
}
if (iterateItemContext) {
if (path.startsWith('//')) {
return makePath(path);
}
const currentItemPath = makeIteratePath('/');
if (path === currentItemPath || path.startsWith(`${currentItemPath}/`)) {
return path;
}
return makeIteratePath(path);
}
return makePath(path);
}, [iterateItemContext, makeIteratePath, makePath, pathType]);
const subscribablePaths = useMemo(() => {
if (!pathProp) {
return undefined;
}
const paths = Array.isArray(pathProp) ? pathProp : [pathProp];
const normalizedPaths = paths.filter(isPath).map(path => resolvePath(path));
return normalizedPaths.length > 0 ? normalizedPaths : undefined;
}, [pathProp, resolvePath]);
const subscribablePath = typeof pathProp === 'string' && subscribablePaths?.length === 1 ? subscribablePaths[0] : undefined;
const subscribe = useCallback(callback => {
if (subscribablePaths?.length && dataContextRef.current?.subscribeDataValue) {
const handleUpdate = () => {
snapshotVersionRef.current += 1;
callback();
};
const unsubscribers = subscribablePaths.map(path => dataContextRef.current.subscribeDataValue(path, handleUpdate));
return () => {
unsubscribers.forEach(unsubscribe => unsubscribe());
};
}
return () => undefined;
}, [dataContextRef, subscribablePaths]);
const getSnapshot = useCallback(() => {
if (subscribablePath) {
return dataContextRef.current?.getDataValue?.(subscribablePath);
}
return snapshotVersionRef.current;
}, [dataContextRef, subscribablePath]);
const subscribedValue = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
const get = useCallback((selector, data = dataContextRef.current?.internalDataRef?.current) => {
if (selector === '/') {
return data;
}
return pointer.has(data, selector) ? pointer.get(data, selector) : undefined;
}, [dataContextRef]);
const getValueByPath = useCallback((path, data = undefined) => {
if (isPath(path)) {
return get(makePath(path), data);
}
}, [get, makePath]);
const getValueByIteratePath = useCallback(path => {
if (isPath(path)) {
return get(makeIteratePath(path));
}
}, [get, makeIteratePath]);
const moveValueToPath = useCallback((path, value, object = {}) => {
if (path !== '/' && isPath(path)) {
pointer.set(object, path, value);
return object;
}
return value;
}, []);
const getData = useCallback((path, options) => {
if (isPath(path)) {
const value = getValueByPath(path);
if (options?.includeCurrentPath && path !== '/') {
return moveValueToPath(path, value);
}
return value;
}
}, [getValueByPath, moveValueToPath]);
const getSourceValue = useCallback(source => {
if (typeof source === 'string' && isPath(source)) {
return get(resolvePath(source));
}
return source;
}, [get, resolvePath]);
if (typeof pathProp === 'string') {
value = subscribablePath ? subscribedValue : getSourceValue(pathProp);
}
return {
getSourceValue,
getValueByPath,
getValueByIteratePath,
moveValueToPath,
getData,
value
};
}
//# sourceMappingURL=useDataValue.js.map