1- import React , { ReactElement , ReactNode , useMemo } from 'react' ;
1+ import React , { ReactElement , ReactNode , useLayoutEffect , useMemo , useRef , useState } from 'react' ;
22import { clsx } from 'clsx' ;
33import { PulseIcon , UsersIcon } from '@/components/ui/icon' ;
44import { Popover , PopoverArrow , PopoverContent , PopoverTrigger } from '@/components/ui/popover' ;
@@ -9,6 +9,7 @@ import { FragmentType, graphql, useFragment } from '@/gql';
99import { SupergraphMetadataList_SupergraphMetadataFragmentFragment } from '@/gql/graphql' ;
1010import { formatNumber , toDecimal } from '@/lib/hooks' ;
1111import { cn } from '@/lib/utils' ;
12+ import { capitalize } from '@/utils' ;
1213import { ChatBubbleIcon } from '@radix-ui/react-icons' ;
1314import { Link as NextLink , useRouter } from '@tanstack/react-router' ;
1415import { useArgumentListToggle , useSchemaExplorerContext } from './provider' ;
@@ -49,6 +50,38 @@ function Description(props: { description: string }) {
4950 ) ;
5051}
5152
53+ export function DescriptionInline ( props : { description : string } ) {
54+ const ref = useRef < HTMLDivElement > ( null ) ;
55+ const [ canExpand , setCanExpand ] = useState ( false ) ;
56+ const [ isExpanded , setIsExpanded ] = useState ( false ) ;
57+
58+ useLayoutEffect ( ( ) => {
59+ if ( ref . current ) {
60+ setCanExpand ( ref . current . scrollHeight > ref . current . clientHeight ) ;
61+ }
62+ } , [ props . description ] ) ;
63+
64+ return (
65+ < div className = "inline-block max-w-screen-sm" >
66+ < Markdown
67+ ref = { ref }
68+ className = { clsx ( 'text-muted-foreground line-clamp-2 text-left text-sm' , {
69+ 'line-clamp-none' : isExpanded ,
70+ } ) }
71+ content = { props . description }
72+ />
73+ { canExpand ? (
74+ < span
75+ className = "cursor-pointer text-xs text-orange-500"
76+ onClick = { ( ) => setIsExpanded ( prev => ! prev ) }
77+ >
78+ { isExpanded ? 'Show less' : 'Show more' }
79+ </ span >
80+ ) : null }
81+ </ div >
82+ ) ;
83+ }
84+
5285const SchemaExplorerUsageStats_UsageFragment = graphql ( `
5386 fragment SchemaExplorerUsageStats_UsageFragment on SchemaCoordinateUsage {
5487 total
@@ -68,10 +101,13 @@ export function SchemaExplorerUsageStats(props: {
68101 organizationSlug : string ;
69102 projectSlug : string ;
70103 targetSlug : string ;
104+ kindLabel ?: string ;
71105} ) {
72106 const usage = useFragment ( SchemaExplorerUsageStats_UsageFragment , props . usage ) ;
73107 const percentage = props . totalRequests ? ( usage . total / props . totalRequests ) * 100 : 0 ;
74108
109+ const kindLabel = useMemo ( ( ) => props . kindLabel ?? 'field' , [ props . kindLabel ] ) ;
110+
75111 return (
76112 < TooltipProvider delayDuration = { 0 } >
77113 < div className = "ml-3 flex flex-row items-center gap-2 text-xs" >
@@ -90,18 +126,19 @@ export function SchemaExplorerUsageStats(props: {
90126 < Tooltip >
91127 < TooltipContent >
92128 < div className = "z-10" >
93- < div className = "mb-1 text-lg font-bold" > Field Usage</ div >
129+ < div className = "mb-1 text-lg font-bold" > { capitalize ( kindLabel ) } Usage</ div >
94130 { usage . isUsed === false ? (
95- < div > This field is currently not in use.</ div >
131+ < div > This { kindLabel } is currently not in use.</ div >
96132 ) : (
97133 < div >
98134 < ul >
99135 < li >
100- This field has been queried in < strong > { formatNumber ( usage . total ) } </ strong > { ' ' }
101- requests.
136+ This { kindLabel } has been queried in{ ' ' }
137+ < strong > { formatNumber ( usage . total ) } </ strong > requests.
102138 </ li >
103139 < li >
104- < strong > { toDecimal ( percentage ) } %</ strong > of all requests use this field.
140+ < strong > { toDecimal ( percentage ) } %</ strong > of all requests use this { kindLabel }
141+ .
105142 </ li >
106143 </ ul >
107144
@@ -159,7 +196,7 @@ export function SchemaExplorerUsageStats(props: {
159196
160197 { Array . isArray ( usage . usedByClients ) && usage . usedByClients . length > 0 ? (
161198 < >
162- < div className = "mb-2" > This field is used by the following clients:</ div >
199+ < div className = "mb-2" > This { kindLabel } is used by the following clients:</ div >
163200 < ul >
164201 { usage . usedByClients . map ( clientName => (
165202 < li key = { clientName } className = "font-bold" >
@@ -180,7 +217,7 @@ export function SchemaExplorerUsageStats(props: {
180217 </ ul >
181218 </ >
182219 ) : (
183- < div > This field is not used by any client.</ div >
220+ < div > This { kindLabel } is not used by any client.</ div >
184221 ) }
185222 </ >
186223 </ TooltipContent >
@@ -308,8 +345,8 @@ export function GraphQLTypeCard(props: {
308345 type = { props . name }
309346 />
310347 </ div >
311- { props . description ? < Description description = { props . description } /> : null }
312348 </ div >
349+ { props . description ? < DescriptionInline description = { props . description } /> : null }
313350 </ div >
314351 { Array . isArray ( props . implements ) && props . implements . length > 0 ? (
315352 < div className = "flex flex-row items-center text-sm text-gray-500" >
@@ -329,6 +366,7 @@ export function GraphQLTypeCard(props: {
329366 ) : null }
330367 { props . usage && typeof props . totalRequests !== 'undefined' ? (
331368 < SchemaExplorerUsageStats
369+ kindLabel = { props . kind }
332370 totalRequests = { props . totalRequests }
333371 usage = { props . usage }
334372 organizationSlug = { props . organizationSlug }
@@ -529,83 +567,88 @@ export function GraphQLFields(props: {
529567
530568 return (
531569 < GraphQLTypeCardListItem key = { field . name } index = { i } >
532- < div >
533- { props . warnAboutUnusedArguments &&
534- isUsed &&
535- hasUnusedArguments &&
536- showsUnusedSchema ? (
537- < Tooltip >
538- < TooltipContent >
539- This field is used but the presented arguments are not.
540- </ TooltipContent >
541- < TooltipTrigger >
542- < span className = "mr-1 text-sm text-orange-500" > *</ span >
543- </ TooltipTrigger >
544- </ Tooltip >
545- ) : null }
546- { props . warnAboutDeprecatedArguments && ! isDeprecated ? (
547- < Tooltip >
548- < TooltipContent >
549- This field is not deprecated but the presented arguments are.
550- </ TooltipContent >
551- < TooltipTrigger >
552- < span className = "mr-1 text-sm text-orange-500" > *</ span >
553- </ TooltipTrigger >
554- </ Tooltip >
555- ) : null }
556- < DeprecationNote
557- styleDeprecated = { props . styleDeprecated }
558- deprecationReason = { field . deprecationReason }
559- >
560- < LinkToCoordinatePage
561- organizationSlug = { props . organizationSlug }
562- projectSlug = { props . projectSlug }
563- targetSlug = { props . targetSlug }
564- coordinate = { coordinate }
565- className = "font-semibold"
566- >
567- { field . name }
568- </ LinkToCoordinatePage >
569- </ DeprecationNote >
570- { field . args . length > 0 ? (
571- < GraphQLArguments
572- organizationSlug = { props . organizationSlug }
573- projectSlug = { props . projectSlug }
574- targetSlug = { props . targetSlug }
575- styleDeprecated = { props . styleDeprecated }
576- parentCoordinate = { coordinate }
577- args = { field . args }
578- />
579- ) : null }
580- < span className = "mr-1" > :</ span >
581- < GraphQLTypeAsLink
582- organizationSlug = { props . organizationSlug }
583- projectSlug = { props . projectSlug }
584- targetSlug = { props . targetSlug }
585- className = "font-semibold text-gray-400"
586- type = { field . type }
587- />
588- </ div >
589- < div className = "flex flex-row items-center" >
590- { field . supergraphMetadata ? (
591- < div className = "ml-1" >
592- < SupergraphMetadataList
593- targetSlug = { props . targetSlug }
594- projectSlug = { props . projectSlug }
570+ < div className = "w-full" >
571+ < div className = "flex w-full flex-row items-center justify-between" >
572+ < div >
573+ { props . warnAboutUnusedArguments &&
574+ isUsed &&
575+ hasUnusedArguments &&
576+ showsUnusedSchema ? (
577+ < Tooltip >
578+ < TooltipContent >
579+ This field is used but the presented arguments are not.
580+ </ TooltipContent >
581+ < TooltipTrigger >
582+ < span className = "mr-1 text-sm text-orange-500" > *</ span >
583+ </ TooltipTrigger >
584+ </ Tooltip >
585+ ) : null }
586+ { props . warnAboutDeprecatedArguments && ! isDeprecated ? (
587+ < Tooltip >
588+ < TooltipContent >
589+ This field is not deprecated but the presented arguments are.
590+ </ TooltipContent >
591+ < TooltipTrigger >
592+ < span className = "mr-1 text-sm text-orange-500" > *</ span >
593+ </ TooltipTrigger >
594+ </ Tooltip >
595+ ) : null }
596+ < DeprecationNote
597+ styleDeprecated = { props . styleDeprecated }
598+ deprecationReason = { field . deprecationReason }
599+ >
600+ < LinkToCoordinatePage
601+ organizationSlug = { props . organizationSlug }
602+ projectSlug = { props . projectSlug }
603+ targetSlug = { props . targetSlug }
604+ coordinate = { coordinate }
605+ className = "font-semibold"
606+ >
607+ { field . name }
608+ </ LinkToCoordinatePage >
609+ </ DeprecationNote >
610+ { field . args . length > 0 ? (
611+ < GraphQLArguments
612+ organizationSlug = { props . organizationSlug }
613+ projectSlug = { props . projectSlug }
614+ targetSlug = { props . targetSlug }
615+ styleDeprecated = { props . styleDeprecated }
616+ parentCoordinate = { coordinate }
617+ args = { field . args }
618+ />
619+ ) : null }
620+ < span className = "mr-1" > :</ span >
621+ < GraphQLTypeAsLink
595622 organizationSlug = { props . organizationSlug }
596- supergraphMetadata = { field . supergraphMetadata }
623+ projectSlug = { props . projectSlug }
624+ targetSlug = { props . targetSlug }
625+ className = "font-semibold text-gray-400"
626+ type = { field . type }
597627 />
598628 </ div >
599- ) : null }
600- { typeof totalRequests === 'number' ? (
601- < SchemaExplorerUsageStats
602- totalRequests = { totalRequests }
603- usage = { field . usage }
604- targetSlug = { props . targetSlug }
605- projectSlug = { props . projectSlug }
606- organizationSlug = { props . organizationSlug }
607- />
608- ) : null }
629+ < div className = "flex flex-row items-center" >
630+ { field . supergraphMetadata ? (
631+ < div className = "ml-1" >
632+ < SupergraphMetadataList
633+ targetSlug = { props . targetSlug }
634+ projectSlug = { props . projectSlug }
635+ organizationSlug = { props . organizationSlug }
636+ supergraphMetadata = { field . supergraphMetadata }
637+ />
638+ </ div >
639+ ) : null }
640+ { typeof totalRequests === 'number' ? (
641+ < SchemaExplorerUsageStats
642+ totalRequests = { totalRequests }
643+ usage = { field . usage }
644+ targetSlug = { props . targetSlug }
645+ projectSlug = { props . projectSlug }
646+ organizationSlug = { props . organizationSlug }
647+ />
648+ ) : null }
649+ </ div >
650+ </ div >
651+ { field . description ? < DescriptionInline description = { field . description } /> : null }
609652 </ div >
610653 </ GraphQLTypeCardListItem >
611654 ) ;
@@ -666,39 +709,44 @@ export function GraphQLInputFields(props: {
666709 const coordinate = `${ props . typeName } .${ field . name } ` ;
667710 return (
668711 < GraphQLTypeCardListItem key = { field . name } index = { i } >
669- < div className = "text-gray-400" >
670- < DeprecationNote
671- styleDeprecated = { props . styleDeprecated }
672- deprecationReason = { field . deprecationReason }
673- >
674- < LinkToCoordinatePage
675- organizationSlug = { props . organizationSlug }
676- projectSlug = { props . projectSlug }
677- targetSlug = { props . targetSlug }
678- coordinate = { coordinate }
679- className = "font-semibold text-white"
680- >
681- { field . name }
682- </ LinkToCoordinatePage >
683- </ DeprecationNote >
684- < span className = "mr-1" > :</ span >
685- < GraphQLTypeAsLink
686- organizationSlug = { props . organizationSlug }
687- projectSlug = { props . projectSlug }
688- targetSlug = { props . targetSlug }
689- className = "font-semibold"
690- type = { field . type }
691- />
712+ < div >
713+ < div className = "flex w-full flex-row items-center justify-between" >
714+ < div className = "text-gray-400" >
715+ < DeprecationNote
716+ styleDeprecated = { props . styleDeprecated }
717+ deprecationReason = { field . deprecationReason }
718+ >
719+ < LinkToCoordinatePage
720+ organizationSlug = { props . organizationSlug }
721+ projectSlug = { props . projectSlug }
722+ targetSlug = { props . targetSlug }
723+ coordinate = { coordinate }
724+ className = "font-semibold text-white"
725+ >
726+ { field . name }
727+ </ LinkToCoordinatePage >
728+ </ DeprecationNote >
729+ < span className = "mr-1" > :</ span >
730+ < GraphQLTypeAsLink
731+ organizationSlug = { props . organizationSlug }
732+ projectSlug = { props . projectSlug }
733+ targetSlug = { props . targetSlug }
734+ className = "font-semibold"
735+ type = { field . type }
736+ />
737+ </ div >
738+ { typeof props . totalRequests === 'number' ? (
739+ < SchemaExplorerUsageStats
740+ totalRequests = { props . totalRequests }
741+ usage = { field . usage }
742+ targetSlug = { props . targetSlug }
743+ projectSlug = { props . projectSlug }
744+ organizationSlug = { props . organizationSlug }
745+ />
746+ ) : null }
747+ </ div >
748+ { field . description ? < DescriptionInline description = { field . description } /> : null }
692749 </ div >
693- { typeof props . totalRequests === 'number' ? (
694- < SchemaExplorerUsageStats
695- totalRequests = { props . totalRequests }
696- usage = { field . usage }
697- targetSlug = { props . targetSlug }
698- projectSlug = { props . projectSlug }
699- organizationSlug = { props . organizationSlug }
700- />
701- ) : null }
702750 </ GraphQLTypeCardListItem >
703751 ) ;
704752 } ) }
0 commit comments