polen 0.10.0-next.21 → 0.10.0-next.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api/config/configurator.d.ts +62 -11
- package/build/api/config/configurator.d.ts.map +1 -1
- package/build/api/config/configurator.js +9 -0
- package/build/api/config/configurator.js.map +1 -1
- package/build/api/vite/plugins/core.d.ts.map +1 -1
- package/build/api/vite/plugins/core.js +1 -0
- package/build/api/vite/plugins/core.js.map +1 -1
- package/build/project-data.d.ts +1 -0
- package/build/project-data.d.ts.map +1 -1
- package/build/sandbox.js +40 -17
- package/build/sandbox.js.map +1 -1
- package/build/template/components/CodeBlock.d.ts.map +1 -1
- package/build/template/components/CodeBlock.js +3 -5
- package/build/template/components/CodeBlock.js.map +1 -1
- package/build/template/components/Field.js +1 -1
- package/build/template/components/Field.js.map +1 -1
- package/build/template/components/GraphQLInteractive/GraphQLInteractive.d.ts +31 -0
- package/build/template/components/GraphQLInteractive/GraphQLInteractive.d.ts.map +1 -0
- package/build/template/components/GraphQLInteractive/GraphQLInteractive.js +275 -0
- package/build/template/components/GraphQLInteractive/GraphQLInteractive.js.map +1 -0
- package/build/template/components/GraphQLInteractive/components/GraphQLErrorBoundary.d.ts +39 -0
- package/build/template/components/GraphQLInteractive/components/GraphQLErrorBoundary.d.ts.map +1 -0
- package/build/template/components/GraphQLInteractive/components/GraphQLErrorBoundary.js +51 -0
- package/build/template/components/GraphQLInteractive/components/GraphQLErrorBoundary.js.map +1 -0
- package/build/template/components/GraphQLInteractive/components/GraphQLTokenPopover.d.ts +33 -0
- package/build/template/components/GraphQLInteractive/components/GraphQLTokenPopover.d.ts.map +1 -0
- package/build/template/components/GraphQLInteractive/components/GraphQLTokenPopover.js +242 -0
- package/build/template/components/GraphQLInteractive/components/GraphQLTokenPopover.js.map +1 -0
- package/build/template/components/GraphQLInteractive/hooks/use-popover-state.d.ts +45 -0
- package/build/template/components/GraphQLInteractive/hooks/use-popover-state.d.ts.map +1 -0
- package/build/template/components/GraphQLInteractive/hooks/use-popover-state.js +176 -0
- package/build/template/components/GraphQLInteractive/hooks/use-popover-state.js.map +1 -0
- package/build/template/components/GraphQLInteractive/index.d.ts +2 -0
- package/build/template/components/GraphQLInteractive/index.d.ts.map +1 -0
- package/build/template/components/GraphQLInteractive/index.js +2 -0
- package/build/template/components/GraphQLInteractive/index.js.map +1 -0
- package/build/template/components/GraphQLInteractive/lib/graphql-node-types.d.ts +52 -0
- package/build/template/components/GraphQLInteractive/lib/graphql-node-types.d.ts.map +1 -0
- package/build/template/components/GraphQLInteractive/lib/graphql-node-types.js +34 -0
- package/build/template/components/GraphQLInteractive/lib/graphql-node-types.js.map +1 -0
- package/build/template/components/GraphQLInteractive/lib/parser.d.ts +71 -0
- package/build/template/components/GraphQLInteractive/lib/parser.d.ts.map +1 -0
- package/build/template/components/GraphQLInteractive/lib/parser.js +836 -0
- package/build/template/components/GraphQLInteractive/lib/parser.js.map +1 -0
- package/build/template/components/GraphQLInteractive/lib/semantic-nodes.d.ts +98 -0
- package/build/template/components/GraphQLInteractive/lib/semantic-nodes.d.ts.map +1 -0
- package/build/template/components/GraphQLInteractive/lib/semantic-nodes.js +31 -0
- package/build/template/components/GraphQLInteractive/lib/semantic-nodes.js.map +1 -0
- package/build/template/components/content/$$.d.ts +0 -1
- package/build/template/components/content/$$.d.ts.map +1 -1
- package/build/template/components/content/$$.js +0 -1
- package/build/template/components/content/$$.js.map +1 -1
- package/package.json +5 -21
- package/src/api/config/configurator.ts +72 -11
- package/src/api/vite/plugins/core.ts +1 -0
- package/src/lib/kit-temp.test.ts +9 -9
- package/src/project-data.ts +1 -0
- package/src/sandbox.ts +40 -17
- package/src/template/components/CodeBlock.tsx +6 -9
- package/src/template/components/Field.tsx +1 -1
- package/src/template/components/GraphQLInteractive/GraphQLInteractive.tsx +464 -0
- package/src/template/components/GraphQLInteractive/components/GraphQLErrorBoundary.tsx +96 -0
- package/src/template/components/GraphQLInteractive/components/GraphQLTokenPopover.tsx +492 -0
- package/src/template/components/GraphQLInteractive/hooks/use-popover-state.ts +244 -0
- package/src/template/components/GraphQLInteractive/index.ts +1 -0
- package/src/template/components/GraphQLInteractive/lib/graphql-node-types.ts +217 -0
- package/src/template/components/GraphQLInteractive/lib/parser.ts +1075 -0
- package/src/template/components/GraphQLInteractive/lib/semantic-nodes.ts +154 -0
- package/src/template/components/GraphQLInteractive/tests/parser-comment.test.ts +33 -0
- package/src/template/components/GraphQLInteractive/tests/parser-error-hint.test.ts +102 -0
- package/src/template/components/GraphQLInteractive/tests/parser.test.ts +131 -0
- package/src/template/components/content/$$.ts +0 -1
- package/build/template/components/content/GraphQLDocumentWithSchema.d.ts +0 -8
- package/build/template/components/content/GraphQLDocumentWithSchema.d.ts.map +0 -1
- package/build/template/components/content/GraphQLDocumentWithSchema.js +0 -13
- package/build/template/components/content/GraphQLDocumentWithSchema.js.map +0 -1
- package/build/template/components/content/GraphQLDocumentWrapper.d.ts +0 -7
- package/build/template/components/content/GraphQLDocumentWrapper.d.ts.map +0 -1
- package/build/template/components/content/GraphQLDocumentWrapper.js +0 -48
- package/build/template/components/content/GraphQLDocumentWrapper.js.map +0 -1
- package/src/template/components/content/GraphQLDocumentWithSchema.tsx +0 -13
- package/src/template/components/content/GraphQLDocumentWrapper.tsx +0 -72
@@ -0,0 +1,492 @@
|
|
1
|
+
/**
|
2
|
+
* Popover component for GraphQL tokens
|
3
|
+
*
|
4
|
+
* Provides rich overlays with type information that can be triggered by hover
|
5
|
+
* and pinned by clicking. Supports multiple simultaneous popovers.
|
6
|
+
*/
|
7
|
+
|
8
|
+
import type { React } from '#dep/react/index'
|
9
|
+
import { Cross1Icon } from '@radix-ui/react-icons'
|
10
|
+
import { Button, Popover } from '@radix-ui/themes'
|
11
|
+
import type { GraphQLToken } from '../lib/parser.js'
|
12
|
+
import {
|
13
|
+
isArgument,
|
14
|
+
isFragment,
|
15
|
+
isInputField,
|
16
|
+
isInvalidField,
|
17
|
+
isOperation,
|
18
|
+
isOutputField,
|
19
|
+
} from '../lib/semantic-nodes.js'
|
20
|
+
|
21
|
+
interface GraphQLTokenPopoverProps {
|
22
|
+
/** The token to create a popover for */
|
23
|
+
token: GraphQLToken
|
24
|
+
|
25
|
+
/** Child element that triggers the popover */
|
26
|
+
children: React.ReactNode
|
27
|
+
|
28
|
+
/** Whether the popover is open */
|
29
|
+
open: boolean
|
30
|
+
|
31
|
+
/** Whether the popover is pinned */
|
32
|
+
pinned: boolean
|
33
|
+
|
34
|
+
/** Called when mouse enters the trigger */
|
35
|
+
onTriggerHover: () => void
|
36
|
+
|
37
|
+
/** Called when mouse leaves the trigger */
|
38
|
+
onTriggerLeave: () => void
|
39
|
+
|
40
|
+
/** Called when trigger is clicked */
|
41
|
+
onTriggerClick: (e: React.MouseEvent) => void
|
42
|
+
|
43
|
+
/** Called when popover content is hovered */
|
44
|
+
onContentHover: () => void
|
45
|
+
|
46
|
+
/** Called when mouse leaves popover content */
|
47
|
+
onContentLeave: () => void
|
48
|
+
|
49
|
+
/** Called when close button is clicked */
|
50
|
+
onClose: () => void
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Get rich content for a token popover
|
55
|
+
*/
|
56
|
+
function getPopoverContent(
|
57
|
+
token: GraphQLToken,
|
58
|
+
): {
|
59
|
+
title: string
|
60
|
+
path?: Array<{ name: string; url: string | null }>
|
61
|
+
description?: string
|
62
|
+
type?: string
|
63
|
+
invalid?: boolean
|
64
|
+
availableFields?: string[]
|
65
|
+
typeName?: string
|
66
|
+
typeUrl?: string
|
67
|
+
fieldName?: string
|
68
|
+
} | null {
|
69
|
+
const semantic = token.semantic
|
70
|
+
|
71
|
+
if (!semantic) return null
|
72
|
+
|
73
|
+
if (isInvalidField(semantic)) {
|
74
|
+
// Get available fields from the parent type
|
75
|
+
const availableFields = Object.keys(semantic.parentType.getFields()).sort()
|
76
|
+
|
77
|
+
// TODO: In the future, check other schema versions to see if the field existed previously
|
78
|
+
// This would help identify renamed or removed fields across schema evolution
|
79
|
+
|
80
|
+
return {
|
81
|
+
title: `Field "${semantic.fieldName}" does not exist`,
|
82
|
+
description: `Type ${semantic.parentType.name} does not have a field named "${semantic.fieldName}".`,
|
83
|
+
typeName: semantic.parentType.name,
|
84
|
+
typeUrl: `/reference/${semantic.parentType.name}`,
|
85
|
+
fieldName: semantic.fieldName,
|
86
|
+
invalid: true,
|
87
|
+
availableFields,
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
if (isOutputField(semantic)) {
|
92
|
+
const fieldType = semantic.fieldDef.type.toString()
|
93
|
+
return {
|
94
|
+
title: semantic.fieldDef.name,
|
95
|
+
path: [
|
96
|
+
{ name: semantic.parentType.name, url: `/reference/${semantic.parentType.name}` },
|
97
|
+
{ name: semantic.fieldDef.name, url: `/reference/${semantic.parentType.name}#${semantic.fieldDef.name}` },
|
98
|
+
],
|
99
|
+
description: semantic.fieldDef.description || undefined,
|
100
|
+
type: `${fieldType}`,
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
if (isInputField(semantic)) {
|
105
|
+
const fieldType = semantic.fieldDef.type.toString()
|
106
|
+
return {
|
107
|
+
title: semantic.fieldDef.name,
|
108
|
+
path: [
|
109
|
+
{ name: semantic.parentType.name, url: `/reference/${semantic.parentType.name}` },
|
110
|
+
{ name: semantic.fieldDef.name, url: `/reference/${semantic.parentType.name}#${semantic.fieldDef.name}` },
|
111
|
+
],
|
112
|
+
description: semantic.fieldDef.description || undefined,
|
113
|
+
type: `${fieldType}`,
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
if (isArgument(semantic)) {
|
118
|
+
const argType = semantic.argumentDef.type.toString()
|
119
|
+
return {
|
120
|
+
title: semantic.argumentDef.name,
|
121
|
+
path: [
|
122
|
+
{ name: semantic.parentType.name, url: `/reference/${semantic.parentType.name}` },
|
123
|
+
{ name: semantic.parentField.name, url: `/reference/${semantic.parentType.name}#${semantic.parentField.name}` },
|
124
|
+
{
|
125
|
+
name: semantic.argumentDef.name,
|
126
|
+
url: `/reference/${semantic.parentType.name}#${semantic.parentField.name}__${semantic.argumentDef.name}`,
|
127
|
+
},
|
128
|
+
],
|
129
|
+
description: semantic.argumentDef.description || undefined,
|
130
|
+
type: `${argType}`,
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
if (isOperation(semantic)) {
|
135
|
+
return {
|
136
|
+
title: semantic.name || `${semantic.type} operation`,
|
137
|
+
path: [
|
138
|
+
{ name: semantic.name || semantic.type, url: null },
|
139
|
+
],
|
140
|
+
description: 'GraphQL Operation',
|
141
|
+
type: semantic.type.charAt(0).toUpperCase() + semantic.type.slice(1),
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
if (isFragment(semantic)) {
|
146
|
+
return {
|
147
|
+
title: semantic.name,
|
148
|
+
path: [
|
149
|
+
{ name: 'fragment', url: null },
|
150
|
+
{ name: semantic.name, url: null },
|
151
|
+
],
|
152
|
+
description: 'GraphQL Fragment',
|
153
|
+
type: `Fragment on ${semantic.onType?.name || 'Unknown'}`,
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
if ('name' in semantic && semantic.name) {
|
158
|
+
// Type reference
|
159
|
+
return {
|
160
|
+
title: semantic.name,
|
161
|
+
path: [
|
162
|
+
{ name: semantic.name, url: `/reference/${semantic.name}` },
|
163
|
+
],
|
164
|
+
description: ('description' in semantic && typeof semantic.description === 'string')
|
165
|
+
? semantic.description
|
166
|
+
: undefined,
|
167
|
+
type: 'GraphQL Type',
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
return null
|
172
|
+
}
|
173
|
+
|
174
|
+
export const GraphQLTokenPopover: React.FC<GraphQLTokenPopoverProps> = ({
|
175
|
+
token,
|
176
|
+
children,
|
177
|
+
open,
|
178
|
+
pinned,
|
179
|
+
onTriggerHover,
|
180
|
+
onTriggerLeave,
|
181
|
+
onTriggerClick,
|
182
|
+
onContentHover,
|
183
|
+
onContentLeave,
|
184
|
+
onClose,
|
185
|
+
}) => {
|
186
|
+
const content = getPopoverContent(token)
|
187
|
+
|
188
|
+
// If no content, just render children without popover
|
189
|
+
if (!content) {
|
190
|
+
return <>{children}</>
|
191
|
+
}
|
192
|
+
|
193
|
+
const popoverContent = (
|
194
|
+
<div
|
195
|
+
style={{
|
196
|
+
padding: 'var(--space-1)',
|
197
|
+
minWidth: '200px',
|
198
|
+
maxWidth: '400px',
|
199
|
+
position: 'relative',
|
200
|
+
}}
|
201
|
+
onMouseEnter={onContentHover}
|
202
|
+
onMouseLeave={onContentLeave}
|
203
|
+
>
|
204
|
+
{/* Header with close button (if pinned) */}
|
205
|
+
<div
|
206
|
+
style={{
|
207
|
+
display: 'flex',
|
208
|
+
justifyContent: 'space-between',
|
209
|
+
alignItems: 'flex-start',
|
210
|
+
marginBottom: '6px',
|
211
|
+
}}
|
212
|
+
>
|
213
|
+
<div style={{ flex: 1 }}>
|
214
|
+
{/* Path with clickable links - not shown for invalid fields */}
|
215
|
+
{content.path && !content.invalid && (
|
216
|
+
<div
|
217
|
+
style={{
|
218
|
+
fontSize: 'var(--font-size-2)',
|
219
|
+
color: 'var(--gray-12)',
|
220
|
+
marginBottom: 'var(--space-1)',
|
221
|
+
}}
|
222
|
+
>
|
223
|
+
{content.path.map((segment, index) => (
|
224
|
+
<span key={index}>
|
225
|
+
{index > 0 && <span style={{ color: 'var(--gray-11)', margin: '0 var(--space-1)' }}>.</span>}
|
226
|
+
{segment.url
|
227
|
+
? (
|
228
|
+
<a
|
229
|
+
href={segment.url}
|
230
|
+
style={{
|
231
|
+
color: '#0969da',
|
232
|
+
textDecoration: 'none',
|
233
|
+
cursor: 'pointer',
|
234
|
+
}}
|
235
|
+
onMouseEnter={(e) => {
|
236
|
+
e.currentTarget.style.textDecoration = 'underline'
|
237
|
+
}}
|
238
|
+
onMouseLeave={(e) => {
|
239
|
+
e.currentTarget.style.textDecoration = 'none'
|
240
|
+
}}
|
241
|
+
onClick={(e) => {
|
242
|
+
e.preventDefault()
|
243
|
+
if (segment.url) {
|
244
|
+
window.location.href = segment.url
|
245
|
+
}
|
246
|
+
}}
|
247
|
+
>
|
248
|
+
{segment.name}
|
249
|
+
</a>
|
250
|
+
)
|
251
|
+
: (
|
252
|
+
<span style={{ color: '#24292e' }}>
|
253
|
+
{segment.name}
|
254
|
+
</span>
|
255
|
+
)}
|
256
|
+
</span>
|
257
|
+
))}
|
258
|
+
</div>
|
259
|
+
)}
|
260
|
+
|
261
|
+
{/* Type info for valid fields */}
|
262
|
+
{content.type && !content.invalid && (
|
263
|
+
<div
|
264
|
+
style={{
|
265
|
+
fontSize: '12px',
|
266
|
+
color: '#6a737d',
|
267
|
+
fontFamily: 'monospace',
|
268
|
+
opacity: 0.8,
|
269
|
+
}}
|
270
|
+
>
|
271
|
+
{content.type}
|
272
|
+
</div>
|
273
|
+
)}
|
274
|
+
|
275
|
+
{/* Error message for invalid fields */}
|
276
|
+
{content.invalid && (
|
277
|
+
<div
|
278
|
+
style={{
|
279
|
+
fontSize: '14px',
|
280
|
+
color: '#d73a49',
|
281
|
+
marginBottom: '4px',
|
282
|
+
}}
|
283
|
+
>
|
284
|
+
<span style={{ fontWeight: 'bold' }}>Error:</span> Type{' '}
|
285
|
+
<a
|
286
|
+
href={content.typeUrl}
|
287
|
+
style={{
|
288
|
+
color: '#0969da',
|
289
|
+
textDecoration: 'none',
|
290
|
+
cursor: 'pointer',
|
291
|
+
}}
|
292
|
+
onMouseEnter={(e) => {
|
293
|
+
e.currentTarget.style.textDecoration = 'underline'
|
294
|
+
}}
|
295
|
+
onMouseLeave={(e) => {
|
296
|
+
e.currentTarget.style.textDecoration = 'none'
|
297
|
+
}}
|
298
|
+
onClick={(e) => {
|
299
|
+
e.preventDefault()
|
300
|
+
if (content.typeUrl) {
|
301
|
+
window.location.href = content.typeUrl
|
302
|
+
}
|
303
|
+
}}
|
304
|
+
>
|
305
|
+
{content.typeName}
|
306
|
+
</a>{' '}
|
307
|
+
does not have a field named "{content.fieldName}".
|
308
|
+
</div>
|
309
|
+
)}
|
310
|
+
</div>
|
311
|
+
|
312
|
+
{pinned && !content.invalid && (
|
313
|
+
<Button
|
314
|
+
variant='ghost'
|
315
|
+
size='1'
|
316
|
+
onClick={onClose}
|
317
|
+
style={{
|
318
|
+
marginLeft: '8px',
|
319
|
+
padding: '2px',
|
320
|
+
minWidth: 'auto',
|
321
|
+
height: 'auto',
|
322
|
+
}}
|
323
|
+
>
|
324
|
+
<Cross1Icon width='12' height='12' />
|
325
|
+
</Button>
|
326
|
+
)}
|
327
|
+
</div>
|
328
|
+
|
329
|
+
{/* Description for valid fields only */}
|
330
|
+
{content.description && !content.invalid && (
|
331
|
+
<div
|
332
|
+
style={{
|
333
|
+
fontSize: '13px',
|
334
|
+
color: '#24292e',
|
335
|
+
lineHeight: '1.4',
|
336
|
+
opacity: 0.9,
|
337
|
+
}}
|
338
|
+
>
|
339
|
+
{content.description}
|
340
|
+
</div>
|
341
|
+
)}
|
342
|
+
|
343
|
+
{/* Available fields in SDL-like format */}
|
344
|
+
{content.invalid && content.availableFields && (
|
345
|
+
<div
|
346
|
+
style={{
|
347
|
+
marginTop: '8px',
|
348
|
+
backgroundColor: '#f6f8fa',
|
349
|
+
border: '1px solid #e1e4e8',
|
350
|
+
borderRadius: '4px',
|
351
|
+
fontSize: '12px',
|
352
|
+
fontFamily: 'monospace',
|
353
|
+
lineHeight: '1.5',
|
354
|
+
overflow: 'hidden',
|
355
|
+
}}
|
356
|
+
>
|
357
|
+
<div style={{ padding: '10px 10px 0 10px' }}>
|
358
|
+
<span style={{ color: '#d73a49' }}>type</span>{' '}
|
359
|
+
<a
|
360
|
+
href={content.typeUrl}
|
361
|
+
style={{
|
362
|
+
color: '#005cc5',
|
363
|
+
fontWeight: 500,
|
364
|
+
textDecoration: 'none',
|
365
|
+
}}
|
366
|
+
onMouseEnter={(e) => {
|
367
|
+
e.currentTarget.style.textDecoration = 'underline'
|
368
|
+
}}
|
369
|
+
onMouseLeave={(e) => {
|
370
|
+
e.currentTarget.style.textDecoration = 'none'
|
371
|
+
}}
|
372
|
+
>
|
373
|
+
{content.typeName}
|
374
|
+
</a>{' '}
|
375
|
+
<span style={{ color: '#24292e' }}>{'{'}</span>
|
376
|
+
</div>
|
377
|
+
<div
|
378
|
+
style={{
|
379
|
+
maxHeight: '200px',
|
380
|
+
overflowY: 'auto',
|
381
|
+
padding: '0 10px',
|
382
|
+
marginRight: '2px', // Account for scrollbar
|
383
|
+
}}
|
384
|
+
>
|
385
|
+
{(() => {
|
386
|
+
// Insert the invalid field in alphabetical position
|
387
|
+
const invalidFieldName = content.fieldName || ''
|
388
|
+
const allFields = [...content.availableFields]
|
389
|
+
const insertIndex = allFields.findIndex(f => f.localeCompare(invalidFieldName) > 0)
|
390
|
+
|
391
|
+
// Create the full list with invalid field inserted
|
392
|
+
const fieldsWithInvalid = insertIndex === -1
|
393
|
+
? [...allFields, invalidFieldName]
|
394
|
+
: [...allFields.slice(0, insertIndex), invalidFieldName, ...allFields.slice(insertIndex)]
|
395
|
+
|
396
|
+
return fieldsWithInvalid.map((field) => {
|
397
|
+
const isInvalidField = field === invalidFieldName
|
398
|
+
return (
|
399
|
+
<div
|
400
|
+
key={field}
|
401
|
+
style={{
|
402
|
+
paddingLeft: isInvalidField ? '26px' : '16px',
|
403
|
+
color: isInvalidField ? '#d73a49' : '#6f42c1',
|
404
|
+
display: 'flex',
|
405
|
+
alignItems: 'center',
|
406
|
+
backgroundColor: isInvalidField ? 'var(--red-a2)' : 'transparent',
|
407
|
+
marginLeft: isInvalidField ? '-10px' : '0',
|
408
|
+
marginRight: isInvalidField ? '-10px' : '0',
|
409
|
+
paddingTop: isInvalidField ? '2px' : '0',
|
410
|
+
paddingBottom: isInvalidField ? '2px' : '0',
|
411
|
+
}}
|
412
|
+
>
|
413
|
+
{isInvalidField
|
414
|
+
? (
|
415
|
+
<span style={{ textDecoration: 'line-through' }}>
|
416
|
+
{field}
|
417
|
+
</span>
|
418
|
+
)
|
419
|
+
: (
|
420
|
+
<a
|
421
|
+
href={`/reference/${content.typeName}#${field}`}
|
422
|
+
style={{
|
423
|
+
color: '#6f42c1',
|
424
|
+
textDecoration: 'none',
|
425
|
+
}}
|
426
|
+
onMouseEnter={(e) => {
|
427
|
+
e.currentTarget.style.textDecoration = 'underline'
|
428
|
+
}}
|
429
|
+
onMouseLeave={(e) => {
|
430
|
+
e.currentTarget.style.textDecoration = 'none'
|
431
|
+
}}
|
432
|
+
>
|
433
|
+
{field}
|
434
|
+
</a>
|
435
|
+
)}
|
436
|
+
{isInvalidField && (
|
437
|
+
<span
|
438
|
+
style={{
|
439
|
+
marginLeft: '12px',
|
440
|
+
color: '#d73a49',
|
441
|
+
fontSize: '11px',
|
442
|
+
display: 'flex',
|
443
|
+
alignItems: 'center',
|
444
|
+
gap: '4px',
|
445
|
+
}}
|
446
|
+
>
|
447
|
+
← No such field
|
448
|
+
</span>
|
449
|
+
)}
|
450
|
+
</div>
|
451
|
+
)
|
452
|
+
})
|
453
|
+
})()}
|
454
|
+
</div>
|
455
|
+
<div style={{ padding: '0 10px 10px 10px', color: '#24292e' }}>{'}'}</div>
|
456
|
+
</div>
|
457
|
+
)}
|
458
|
+
</div>
|
459
|
+
)
|
460
|
+
|
461
|
+
return (
|
462
|
+
<Popover.Root open={open}>
|
463
|
+
<Popover.Trigger>
|
464
|
+
<span
|
465
|
+
style={{ cursor: token.polen.isInteractive() ? 'pointer' : 'default' }}
|
466
|
+
onMouseEnter={onTriggerHover}
|
467
|
+
onMouseLeave={onTriggerLeave}
|
468
|
+
onClick={onTriggerClick}
|
469
|
+
>
|
470
|
+
{children}
|
471
|
+
</span>
|
472
|
+
</Popover.Trigger>
|
473
|
+
|
474
|
+
<Popover.Content
|
475
|
+
side='top'
|
476
|
+
align='start'
|
477
|
+
sideOffset={4}
|
478
|
+
style={{
|
479
|
+
padding: 'var(--space-2)',
|
480
|
+
background: content.invalid ? 'var(--red-1)' : 'var(--color-background)',
|
481
|
+
border: content.invalid ? '2px solid var(--red-7)' : '1px solid var(--gray-6)',
|
482
|
+
borderRadius: 'var(--radius-1)',
|
483
|
+
boxShadow: 'none',
|
484
|
+
zIndex: 1000,
|
485
|
+
maxWidth: '450px',
|
486
|
+
}}
|
487
|
+
>
|
488
|
+
{popoverContent}
|
489
|
+
</Popover.Content>
|
490
|
+
</Popover.Root>
|
491
|
+
)
|
492
|
+
}
|