import React, { useCallback, useState } from 'react';
import JSONArrow from './JSONArrow';
import getCollectionEntries from './getCollectionEntries';
import JSONNode from './JSONNode';
import ItemRange from './ItemRange';
import type { CircularCache, CommonInternalProps } from './types';
/**
* Renders nested values (eg. objects, arrays, lists, etc.)
*/
export interface RenderChildNodesProps extends CommonInternalProps {
data: unknown;
nodeType: string;
circularCache: CircularCache;
level: number;
}
interface Range {
from: number;
to: number;
}
interface Entry {
key: string | number;
value: unknown;
}
function isRange(rangeOrEntry: Range | Entry): rangeOrEntry is Range {
return (rangeOrEntry as Range).to !== undefined;
}
function renderChildNodes(
props: RenderChildNodesProps,
from?: number,
to?: number,
) {
const {
nodeType,
data,
collectionLimit,
circularCache,
keyPath,
postprocessValue,
sortObjectKeys,
} = props;
const childNodes: React.ReactNode[] = [];
getCollectionEntries(
nodeType,
data,
sortObjectKeys,
collectionLimit,
from,
to,
).forEach((entry) => {
if (isRange(entry)) {
childNodes.push(
,
);
} else {
const { key, value } = entry;
const isCircular = circularCache.indexOf(value) !== -1;
childNodes.push(
,
);
}
});
return childNodes;
}
interface Props extends CommonInternalProps {
data: unknown;
nodeType: string;
nodeTypeIndicator: string;
createItemString: (data: unknown, collectionLimit: number) => string;
expandable: boolean;
}
export default function JSONNestedNode(props: Props) {
const {
circularCache = [],
collectionLimit,
createItemString,
data,
expandable,
getItemString,
hideRoot,
isCircular,
keyPath,
labelRenderer,
level = 0,
nodeType,
nodeTypeIndicator,
shouldExpandNodeInitially,
styling,
} = props;
const [expanded, setExpanded] = useState(
// calculate individual node expansion if necessary
isCircular ? false : shouldExpandNodeInitially(keyPath, data, level),
);
const handleClick = useCallback(() => {
if (expandable) setExpanded(!expanded);
}, [expandable, expanded]);
const renderedChildren =
expanded || (hideRoot && level === 0)
? renderChildNodes({ ...props, circularCache, level: level + 1 })
: null;
const itemType = (
{nodeTypeIndicator}
);
const renderedItemString = getItemString(
nodeType,
data,
itemType,
createItemString(data, collectionLimit),
keyPath,
);
const stylingArgs = [keyPath, nodeType, expanded, expandable] as const;
return hideRoot ? (
) : (
{expandable && (
)}
{renderedItemString}
);
}