@bestcodetools/graphql-playground 0.0.0

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.
@@ -0,0 +1,644 @@
1
+ ((app) => {
2
+ const TEMPLATE_VERSION = '20260409g';
3
+
4
+ app.component('fieldType', {
5
+ templateUrl: `components/field-type.html?v=${TEMPLATE_VERSION}`,
6
+ bindings: {
7
+ fieldRef: '<',
8
+ onShowTooltip: '&?',
9
+ onHideTooltip: '&?'
10
+ },
11
+ controller: [function () {
12
+ const $ctrl = this;
13
+ $ctrl.onTypeAnchorClick = function ($event) {
14
+ $event.preventDefault();
15
+ const anchorElement = $event.currentTarget;
16
+ const targetId = anchorElement.getAttribute('href').substring(1);
17
+ const targetElement = document.getElementById(targetId);
18
+ if (targetElement) {
19
+ targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
20
+ }
21
+ };
22
+
23
+ $ctrl.getTypeRef = function () {
24
+ return $ctrl.fieldRef && $ctrl.fieldRef.type ? $ctrl.fieldRef.type : $ctrl.fieldRef;
25
+ };
26
+
27
+ $ctrl.isListType = function (typeRef) {
28
+ return typeRef && typeRef.kind === 'LIST';
29
+ };
30
+
31
+ $ctrl.isNonNullType = function (typeRef) {
32
+ return typeRef && typeRef.kind === 'NON_NULL';
33
+ };
34
+
35
+ $ctrl.getTypeLabel = function (typeRef) {
36
+ if (!typeRef) {
37
+ return '';
38
+ }
39
+
40
+ if ($ctrl.isNonNullType(typeRef) || $ctrl.isListType(typeRef)) {
41
+ return $ctrl.getTypeLabel(typeRef.ofType);
42
+ }
43
+
44
+ return typeRef.name || (typeRef.ofType && $ctrl.getTypeLabel(typeRef.ofType)) || '';
45
+ };
46
+
47
+ $ctrl.getNamedType = function (typeRef) {
48
+ if (!typeRef) {
49
+ return null;
50
+ }
51
+
52
+ if (typeRef.name) {
53
+ return typeRef;
54
+ }
55
+
56
+ return typeRef.ofType ? $ctrl.getNamedType(typeRef.ofType) : null;
57
+ };
58
+
59
+ $ctrl.hasNavigableType = function (typeRef) {
60
+ const namedType = $ctrl.getNamedType(typeRef);
61
+
62
+ return Boolean(namedType && ['OBJECT', 'ENUM', 'INTERFACE', 'UNION', 'INPUT_OBJECT', 'SCALAR'].includes(namedType.kind));
63
+ };
64
+
65
+ $ctrl.getTypeAnchor = function (typeRef) {
66
+ const namedType = $ctrl.getNamedType(typeRef);
67
+
68
+ if (!namedType) {
69
+ return '';
70
+ }
71
+
72
+ return `${namedType.kind}_${namedType.name}`;
73
+ };
74
+
75
+ $ctrl.showTooltip = function ($event) {
76
+ if (!$ctrl.onShowTooltip) {
77
+ return;
78
+ }
79
+
80
+ $ctrl.onShowTooltip({
81
+ $event,
82
+ payload: {
83
+ label: $ctrl.getTypeLabel($ctrl.getTypeRef()),
84
+ labelClass: 'type',
85
+ typeRef: $ctrl.getTypeRef(),
86
+ description: ($ctrl.fieldRef && $ctrl.fieldRef.description) || ''
87
+ }
88
+ });
89
+ };
90
+
91
+ $ctrl.hideTooltip = function () {
92
+ if ($ctrl.onHideTooltip) {
93
+ $ctrl.onHideTooltip();
94
+ }
95
+ };
96
+
97
+ $ctrl.forwardShowTooltip = function ($event, payload) {
98
+ if ($ctrl.onShowTooltip) {
99
+ $ctrl.onShowTooltip({ $event, payload });
100
+ }
101
+ };
102
+
103
+ $ctrl.forwardHideTooltip = function () {
104
+ if ($ctrl.onHideTooltip) {
105
+ $ctrl.onHideTooltip();
106
+ }
107
+ };
108
+ }]
109
+ });
110
+
111
+ app.component('fieldArgs', {
112
+ templateUrl: `components/field-args.html?v=${TEMPLATE_VERSION}`,
113
+ bindings: {
114
+ fieldArgs: '<',
115
+ onShowTooltip: '&?',
116
+ onHideTooltip: '&?'
117
+ },
118
+ controller: [function () {
119
+ const $ctrl = this;
120
+
121
+ $ctrl.hasArgs = function () {
122
+ return Array.isArray($ctrl.fieldArgs) && $ctrl.fieldArgs.length > 0;
123
+ };
124
+
125
+ $ctrl.showArgTooltip = function ($event, arg) {
126
+ if (!$ctrl.onShowTooltip) {
127
+ return;
128
+ }
129
+
130
+ $ctrl.onShowTooltip({
131
+ $event,
132
+ payload: {
133
+ label: arg.name,
134
+ labelClass: 'argument',
135
+ typeRef: arg.type,
136
+ description: arg.description || ''
137
+ }
138
+ });
139
+ };
140
+
141
+ $ctrl.forwardShowTooltip = function ($event, payload) {
142
+ if ($ctrl.onShowTooltip) {
143
+ $ctrl.onShowTooltip({ $event, payload });
144
+ }
145
+ };
146
+
147
+ $ctrl.hideTooltip = function () {
148
+ if ($ctrl.onHideTooltip) {
149
+ $ctrl.onHideTooltip();
150
+ }
151
+ };
152
+ }]
153
+ });
154
+ app.component('schemaViewer', {
155
+ templateUrl: `components/schema-viewer.html?v=${TEMPLATE_VERSION}`,
156
+ bindings: {
157
+ schema: '<',
158
+ loadError: '<',
159
+ retryLoadSchema: '&',
160
+ insertSchemaOperation: '&?'
161
+ },
162
+ controller:['$scope', 'I18nService', function (scope, I18nService) {
163
+ const $ctrl = this;
164
+ scope.$ctrl = $ctrl;
165
+ $ctrl.t = function (key) {
166
+ return I18nService.t(key);
167
+ };
168
+ $ctrl.tooltip = {
169
+ visible: false,
170
+ top: 0,
171
+ left: 0,
172
+ label: '',
173
+ labelClass: 'field',
174
+ typeTokens: [],
175
+ description: ''
176
+ };
177
+ $ctrl.searchTerm = '';
178
+ $ctrl.open = sessionStorage.getItem('schemaViewerOpen') === 'true';
179
+
180
+ $ctrl.toggleSchemaViewer = function () {
181
+ $ctrl.open = !$ctrl.open;
182
+ sessionStorage.setItem('schemaViewerOpen', `${$ctrl.open}`);
183
+ };
184
+
185
+ $ctrl.onTypeAnchorClick = function ($event) {
186
+ $event.preventDefault();
187
+ const anchorElement = $event.currentTarget;
188
+ const targetId = anchorElement.getAttribute('href').substring(1);
189
+ const targetElement = document.getElementById(targetId);
190
+ if (targetElement) {
191
+ targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
192
+ }
193
+ };
194
+
195
+ $ctrl.buildTypeTokens = function (typeRef) {
196
+ if (!typeRef) {
197
+ return [];
198
+ }
199
+
200
+ if (typeRef.kind === 'LIST') {
201
+ return [
202
+ { text: '[', className: 'field-type-prefix' },
203
+ ...$ctrl.buildTypeTokens(typeRef.ofType),
204
+ { text: ']', className: 'field-type-suffix' }
205
+ ];
206
+ }
207
+
208
+ if (typeRef.kind === 'NON_NULL') {
209
+ return [
210
+ ...$ctrl.buildTypeTokens(typeRef.ofType),
211
+ { text: '!', className: 'field-type-non-null' }
212
+ ];
213
+ }
214
+
215
+ return [{
216
+ text: typeRef.name || '',
217
+ className: 'field-type'
218
+ }];
219
+ };
220
+
221
+ $ctrl.getNamedType = function (typeRef) {
222
+ if (!typeRef) {
223
+ return null;
224
+ }
225
+
226
+ if (typeRef.name) {
227
+ return typeRef;
228
+ }
229
+
230
+ return typeRef.ofType ? $ctrl.getNamedType(typeRef.ofType) : null;
231
+ };
232
+
233
+ $ctrl.typeRefToString = function (typeRef) {
234
+ if (!typeRef) {
235
+ return '';
236
+ }
237
+
238
+ if (typeRef.kind === 'NON_NULL') {
239
+ return `${$ctrl.typeRefToString(typeRef.ofType)}!`;
240
+ }
241
+
242
+ if (typeRef.kind === 'LIST') {
243
+ return `[${$ctrl.typeRefToString(typeRef.ofType)}]`;
244
+ }
245
+
246
+ return typeRef.name || '';
247
+ };
248
+
249
+ $ctrl.getTypeMap = function () {
250
+ if (!$ctrl.schema || !Array.isArray($ctrl.schema.types)) {
251
+ return new Map();
252
+ }
253
+
254
+ return new Map($ctrl.schema.types.filter((type) => type && type.name).map((type) => [type.name, type]));
255
+ };
256
+
257
+ $ctrl.getOperationForType = function (typeName) {
258
+ if (!$ctrl.schema || !typeName) {
259
+ return null;
260
+ }
261
+
262
+ if ($ctrl.schema.queryType && $ctrl.schema.queryType.name === typeName) {
263
+ return 'query';
264
+ }
265
+
266
+ if ($ctrl.schema.mutationType && $ctrl.schema.mutationType.name === typeName) {
267
+ return 'mutation';
268
+ }
269
+
270
+ if ($ctrl.schema.subscriptionType && $ctrl.schema.subscriptionType.name === typeName) {
271
+ return 'subscription';
272
+ }
273
+
274
+ return null;
275
+ };
276
+
277
+ $ctrl.hasRequiredArguments = function (field) {
278
+ return Boolean(field && Array.isArray(field.args) && field.args.some((arg) => arg && arg.type && arg.type.kind === 'NON_NULL'));
279
+ };
280
+
281
+ $ctrl.supportsSelectionSet = function (field) {
282
+ const namedType = $ctrl.getNamedType(field && field.type);
283
+ return Boolean(namedType && ['OBJECT', 'INTERFACE'].includes(namedType.kind));
284
+ };
285
+
286
+ $ctrl.toVariableNameSuffix = function (typeRef) {
287
+ const typeText = $ctrl.typeRefToString(typeRef);
288
+
289
+ if (!typeText) {
290
+ return 'Value';
291
+ }
292
+
293
+ return typeText
294
+ .replace(/[\[\]!]/g, ' ')
295
+ .trim()
296
+ .split(/\s+/)
297
+ .filter(Boolean)
298
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
299
+ .join('') || 'Value';
300
+ };
301
+
302
+ $ctrl.buildOperationArgumentBindings = function (args) {
303
+ const variableRegistry = new Map();
304
+ const bindings = [];
305
+
306
+ (args || []).forEach((arg) => {
307
+ if (!arg || !arg.name) {
308
+ return;
309
+ }
310
+
311
+ const typeString = $ctrl.typeRefToString(arg.type);
312
+ const baseVariableName = arg.name;
313
+ const existingType = variableRegistry.get(baseVariableName);
314
+ let variableName = baseVariableName;
315
+
316
+ if (existingType && existingType !== typeString) {
317
+ variableName = `${baseVariableName}${$ctrl.toVariableNameSuffix(arg.type)}`;
318
+
319
+ let duplicateIndex = 2;
320
+ while (variableRegistry.has(variableName) && variableRegistry.get(variableName) !== typeString) {
321
+ variableName = `${baseVariableName}${$ctrl.toVariableNameSuffix(arg.type)}${duplicateIndex}`;
322
+ duplicateIndex += 1;
323
+ }
324
+ }
325
+
326
+ if (!variableRegistry.has(variableName)) {
327
+ variableRegistry.set(variableName, typeString);
328
+ }
329
+
330
+ bindings.push({
331
+ argumentName: arg.name,
332
+ variableName,
333
+ typeString,
334
+ typeRef: arg.type
335
+ });
336
+ });
337
+
338
+ return bindings;
339
+ };
340
+
341
+ $ctrl.buildDefaultValue = function (typeRef) {
342
+ if (!typeRef) {
343
+ return null;
344
+ }
345
+
346
+ if (typeRef.kind === 'NON_NULL') {
347
+ return $ctrl.buildDefaultValue(typeRef.ofType);
348
+ }
349
+
350
+ if (typeRef.kind === 'LIST') {
351
+ return [];
352
+ }
353
+
354
+ const namedType = $ctrl.getNamedType(typeRef);
355
+ const schemaType = namedType ? $ctrl.getTypeMap().get(namedType.name) : null;
356
+
357
+ if (!schemaType) {
358
+ return null;
359
+ }
360
+
361
+ if (schemaType.kind === 'INPUT_OBJECT') {
362
+ const result = {};
363
+
364
+ (schemaType.inputFields || []).forEach((field) => {
365
+ if (field && field.type && field.type.kind === 'NON_NULL') {
366
+ result[field.name] = $ctrl.buildDefaultValue(field.type);
367
+ }
368
+ });
369
+
370
+ return result;
371
+ }
372
+
373
+ if (schemaType.kind === 'ENUM') {
374
+ return schemaType.enumValues && schemaType.enumValues.length ? schemaType.enumValues[0].name : null;
375
+ }
376
+
377
+ switch (namedType.name) {
378
+ case 'Int':
379
+ case 'Float':
380
+ return 0;
381
+ case 'Boolean':
382
+ return false;
383
+ case 'String':
384
+ case 'ID':
385
+ return '';
386
+ default:
387
+ return null;
388
+ }
389
+ };
390
+
391
+ $ctrl.buildExpandedSelectionSet = function (typeRef, indentLevel, visitedTypes) {
392
+ const namedType = $ctrl.getNamedType(typeRef);
393
+ const schemaType = namedType ? $ctrl.getTypeMap().get(namedType.name) : null;
394
+
395
+ if (!schemaType || !Array.isArray(schemaType.fields) || !schemaType.fields.length) {
396
+ return [];
397
+ }
398
+
399
+ const nextVisited = new Set(visitedTypes || []);
400
+ if (namedType && namedType.name) {
401
+ nextVisited.add(namedType.name);
402
+ }
403
+
404
+ return schemaType.fields
405
+ .filter((childField) => childField && childField.name && !$ctrl.hasRequiredArguments(childField))
406
+ .flatMap((childField) => {
407
+ if (!$ctrl.supportsSelectionSet(childField)) {
408
+ return [`${indentLevel}${childField.name}`];
409
+ }
410
+
411
+ const childNamedType = $ctrl.getNamedType(childField.type);
412
+ if (!childNamedType || nextVisited.has(childNamedType.name)) {
413
+ return [];
414
+ }
415
+
416
+ const nestedLines = $ctrl.buildExpandedSelectionSet(childField.type, `${indentLevel} `, nextVisited);
417
+
418
+ if (!nestedLines.length) {
419
+ return [];
420
+ }
421
+
422
+ return [
423
+ `${indentLevel}${childField.name} {`,
424
+ ...nestedLines,
425
+ `${indentLevel}}`
426
+ ];
427
+ });
428
+ };
429
+
430
+ $ctrl.buildOperationSnippet = function (field, parentTypeName) {
431
+ const operation = $ctrl.getOperationForType(parentTypeName);
432
+
433
+ if (!operation || !field) {
434
+ return null;
435
+ }
436
+
437
+ const fieldIndent = ' ';
438
+ const childIndent = ' ';
439
+ const argumentBindings = $ctrl.buildOperationArgumentBindings(field.args);
440
+ const variableDefinitions = argumentBindings.length
441
+ ? ` (${argumentBindings.map((binding) => `$${binding.variableName}: ${binding.typeString}`).join(', ')})`
442
+ : '';
443
+ const fieldArguments = argumentBindings.length
444
+ ? `(${argumentBindings.map((binding) => `${binding.argumentName}: $${binding.variableName}`).join(', ')})`
445
+ : '';
446
+ const fieldCall = `${field.name}${fieldArguments}`;
447
+ const expandedLines = $ctrl.supportsSelectionSet(field)
448
+ ? $ctrl.buildExpandedSelectionSet(field.type, childIndent, new Set())
449
+ : [];
450
+ const query = $ctrl.supportsSelectionSet(field)
451
+ ? `${operation}${variableDefinitions} {\n${fieldIndent}${fieldCall} {\n${expandedLines.length ? `${expandedLines.join('\n')}\n` : `${childIndent}\n`}${fieldIndent}}\n}`
452
+ : `${operation}${variableDefinitions} {\n${fieldIndent}${fieldCall}\n}`;
453
+ const variablesObject = {};
454
+
455
+ argumentBindings.forEach((binding) => {
456
+ variablesObject[binding.variableName] = $ctrl.buildDefaultValue(binding.typeRef);
457
+ });
458
+
459
+ return {
460
+ query,
461
+ variables: JSON.stringify(variablesObject, null, 2)
462
+ };
463
+ };
464
+
465
+ $ctrl.tryInsertFieldOperation = function ($event, parentType, field) {
466
+ if (!$event || (!$event.ctrlKey && !$event.metaKey)) {
467
+ return;
468
+ }
469
+
470
+ const snippet = $ctrl.buildOperationSnippet(field, parentType && parentType.name);
471
+
472
+ if (!snippet || !$ctrl.insertSchemaOperation) {
473
+ return;
474
+ }
475
+
476
+ $event.preventDefault();
477
+ $event.stopPropagation();
478
+ $ctrl.insertSchemaOperation({ snippet });
479
+ };
480
+
481
+ $ctrl.onFieldMouseDown = function ($event, parentType, field) {
482
+ $ctrl.tryInsertFieldOperation($event, parentType, field);
483
+ };
484
+
485
+
486
+ $ctrl.getTypeChildren = function (type) {
487
+ if (!type) {
488
+ return [];
489
+ }
490
+
491
+ if (Array.isArray(type.fields)) {
492
+ return type.fields.map((field) => field && field.name).filter(Boolean);
493
+ }
494
+
495
+ if (Array.isArray(type.inputFields)) {
496
+ return type.inputFields.map((field) => field && field.name).filter(Boolean);
497
+ }
498
+
499
+ if (Array.isArray(type.enumValues)) {
500
+ return type.enumValues.map((value) => value && value.name).filter(Boolean);
501
+ }
502
+
503
+ if (Array.isArray(type.possibleTypes)) {
504
+ return type.possibleTypes.map((value) => value && value.name).filter(Boolean);
505
+ }
506
+
507
+ return [];
508
+ };
509
+
510
+ $ctrl.matchesSearch = function (type) {
511
+ const term = ($ctrl.searchTerm || '').trim().toLowerCase();
512
+
513
+ if (!term) {
514
+ return true;
515
+ }
516
+
517
+ if ((type.name || '').toLowerCase().includes(term)) {
518
+ return true;
519
+ }
520
+
521
+ return $ctrl.getTypeChildren(type).some((childName) => childName.toLowerCase().includes(term));
522
+ };
523
+
524
+ $ctrl.typeMatchesSearch = function (type) {
525
+ const term = ($ctrl.searchTerm || '').trim().toLowerCase();
526
+
527
+ if (!term) {
528
+ return true;
529
+ }
530
+
531
+ return (type && type.name ? type.name.toLowerCase().includes(term) : false);
532
+ };
533
+
534
+ $ctrl.fieldMatchesSearch = function (field) {
535
+ const term = ($ctrl.searchTerm || '').trim().toLowerCase();
536
+
537
+ if (!term) {
538
+ return true;
539
+ }
540
+
541
+ return Boolean(field && field.name && field.name.toLowerCase().includes(term));
542
+ };
543
+
544
+ $ctrl.shouldShowField = function (type, field) {
545
+ const term = ($ctrl.searchTerm || '').trim();
546
+
547
+ if (!term) {
548
+ return true;
549
+ }
550
+
551
+ if ($ctrl.typeMatchesSearch(type)) {
552
+ return true;
553
+ }
554
+
555
+ return $ctrl.fieldMatchesSearch(field);
556
+ };
557
+
558
+ $ctrl.getFilteredTypes = function () {
559
+ if (!$ctrl.schema || !Array.isArray($ctrl.schema.types)) {
560
+ return [];
561
+ }
562
+
563
+ return $ctrl.schema.types.filter((type) => type && $ctrl.matchesSearch(type));
564
+ };
565
+
566
+ $ctrl.showTooltip = function ($event, payload) {
567
+ if (!payload) {
568
+ return;
569
+ }
570
+
571
+ const eventTarget = ($event && ($event.currentTarget || $event.target)) || null;
572
+
573
+ if (!eventTarget || typeof eventTarget.getBoundingClientRect !== 'function') {
574
+ return;
575
+ }
576
+
577
+ const rect = eventTarget.getBoundingClientRect();
578
+ const tooltipWidth = 320;
579
+ const gap = 12;
580
+ const maxLeft = Math.max(gap, window.innerWidth - tooltipWidth - gap);
581
+ const top = Math.min(window.innerHeight - 120, rect.bottom + gap);
582
+ const left = Math.min(rect.left, maxLeft);
583
+
584
+ $ctrl.tooltip.visible = true;
585
+ $ctrl.tooltip.top = Math.max(gap, top);
586
+ $ctrl.tooltip.left = Math.max(gap, left);
587
+ $ctrl.tooltip.label = payload.label || '';
588
+ $ctrl.tooltip.labelClass = payload.labelClass || 'field';
589
+ $ctrl.tooltip.typeTokens = $ctrl.buildTypeTokens(payload.typeRef);
590
+ $ctrl.tooltip.description = payload.description || '';
591
+
592
+ if (!scope.$$phase) {
593
+ scope.$applyAsync();
594
+ }
595
+ };
596
+
597
+ $ctrl.hideTooltip = function () {
598
+ $ctrl.tooltip.visible = false;
599
+
600
+ if (!scope.$$phase) {
601
+ scope.$applyAsync();
602
+ }
603
+ };
604
+
605
+ $ctrl.forwardTooltip = function ($event, payload) {
606
+ $ctrl.showTooltip($event, payload);
607
+ };
608
+
609
+ $ctrl.showFieldTooltip = function ($event, field) {
610
+ $ctrl.showTooltip($event, {
611
+ label: field.name,
612
+ labelClass: 'field',
613
+ typeRef: field.type,
614
+ description: field.description || ''
615
+ });
616
+ };
617
+
618
+ $ctrl.showInputFieldTooltip = function ($event, inputField) {
619
+ $ctrl.showTooltip($event, {
620
+ label: inputField.name,
621
+ labelClass: 'argument',
622
+ typeRef: inputField.type,
623
+ description: inputField.description || ''
624
+ });
625
+ };
626
+
627
+ $ctrl.showEnumTooltip = function ($event, value) {
628
+ $ctrl.showTooltip($event, {
629
+ label: value.name,
630
+ labelClass: 'enum',
631
+ description: value.description || ''
632
+ });
633
+ };
634
+
635
+ $ctrl.showPossibleTypeTooltip = function ($event, possibleType) {
636
+ $ctrl.showTooltip($event, {
637
+ label: possibleType.name,
638
+ labelClass: 'type',
639
+ description: ''
640
+ });
641
+ };
642
+ }]
643
+ });
644
+ }) (angular.module('app'));