import { isMirroredNode, isReferenceNode, isRegularNode, SchemaNode } from '@stoplight/json-schema-tree'; import { Box, Flex, NodeAnnotation, Select, SpaceVals, VStack } from '@stoplight/mosaic'; import type { ChangeType } from '@stoplight/types'; import { Atom } from 'jotai'; import { useAtomValue, useUpdateAtom } from 'jotai/utils'; import last from 'lodash/last.js'; import * as React from 'react'; import { COMBINER_NAME_MAP } from '../../consts'; import { useJSVOptionsContext } from '../../contexts'; import { getNodeId, getOriginalNodeId } from '../../hash'; import { isPropertyRequired, visibleChildren } from '../../tree'; import { extractVendorExtensions } from '../../utils/extractVendorExtensions'; import { Caret, Description, getValidationsFromSchema, Types, Validations } from '../shared'; import { ChildStack } from '../shared/ChildStack'; import { Error } from '../shared/Error'; import { Properties, useHasProperties } from '../shared/Properties'; import { hoveredNodeAtom, isNodeHoveredAtom } from './state'; import { useChoices } from './useChoices'; export interface SchemaRowProps { schemaNode: SchemaNode; nestingLevel: number; pl?: SpaceVals; parentNodeId?: string; parentChangeType?: ChangeType; } export const SchemaRow: React.FunctionComponent = React.memo( ({ schemaNode, nestingLevel, pl, parentNodeId, parentChangeType }) => { const { defaultExpandedDepth, renderRowAddon, renderExtensionAddon, onGoToRef, hideExamples, renderRootTreeLines, nodeHasChanged, viewMode, } = useJSVOptionsContext(); const setHoveredNode = useUpdateAtom(hoveredNodeAtom); const nodeId = getNodeId(schemaNode, parentNodeId); // @ts-expect-error originalFragment does exist... const originalNodeId = schemaNode.originalFragment?.$ref ? getOriginalNodeId(schemaNode, parentNodeId) : nodeId; const mode = viewMode === 'standalone' ? undefined : viewMode; const hasChanged = nodeHasChanged?.({ nodeId: originalNodeId, mode }); const [isExpanded, setExpanded] = React.useState( !isMirroredNode(schemaNode) && nestingLevel <= defaultExpandedDepth, ); const { selectedChoice, setSelectedChoice, choices } = useChoices(schemaNode); const typeToShow = selectedChoice.type; const description = isRegularNode(typeToShow) ? typeToShow.annotations.description : null; const rootLevel = renderRootTreeLines ? 1 : 2; const childNodes = React.useMemo(() => visibleChildren(typeToShow), [typeToShow]); const combiner = isRegularNode(schemaNode) && schemaNode.combiners?.length ? schemaNode.combiners[0] : null; const isCollapsible = childNodes.length > 0; const isRootLevel = nestingLevel < rootLevel; const required = isPropertyRequired(schemaNode); const deprecated = isRegularNode(schemaNode) && schemaNode.deprecated; const validations = isRegularNode(schemaNode) ? schemaNode.validations : {}; const hasProperties = useHasProperties({ required, deprecated, validations }); const [totalVendorExtensions, vendorExtensions] = React.useMemo( () => extractVendorExtensions(schemaNode.fragment), [schemaNode.fragment], ); const hasVendorProperties = totalVendorExtensions > 0; const annotationRootOffset = renderRootTreeLines ? 0 : 8; let annotationLeftOffset = -20 - annotationRootOffset; if (nestingLevel > 1) { // annotationLeftOffset -= 27; annotationLeftOffset = -1 * 29 * Math.max(nestingLevel - 1, 1) - Math.min(nestingLevel, 2) * 2 - 16 - annotationRootOffset; if (!renderRootTreeLines) { annotationLeftOffset += 27; } } if (parentChangeType === 'added' && hasChanged && hasChanged.type === 'removed') { return null; } if (parentChangeType === 'removed' && hasChanged && hasChanged.type === 'added') { return null; } return ( <> { e.stopPropagation(); setHoveredNode(selectedChoice.type); }} > {!isRootLevel && } {parentChangeType !== 'added' && parentChangeType !== 'removed' ? ( ) : null} setExpanded(!isExpanded) : undefined} cursor={isCollapsible ? 'pointer' : undefined} > {isCollapsible ? : null} {schemaNode.subpath.length > 0 && shouldShowPropertyName(schemaNode) && ( {last(schemaNode.subpath)} )} {choices.length === 1 && } {onGoToRef && isReferenceNode(schemaNode) && schemaNode.external ? ( { e.preventDefault(); e.stopPropagation(); onGoToRef(schemaNode); }} > (go to ref) ) : null} {schemaNode.subpath.length > 1 && schemaNode.subpath[0] === 'patternProperties' ? ( (pattern property) ) : null} {choices.length > 1 && (