@tinacms/app 2.2.2 → 2.2.4

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.
@@ -1,36 +1,36 @@
1
- import React from 'react'
2
- import * as G from 'graphql'
3
- import { getIn } from 'final-form'
4
- import { z } from 'zod'
5
1
  // @ts-expect-error
6
- import schemaJson from 'SCHEMA_IMPORT'
7
- import { expandQuery, isConnectionType, isNodeType } from './expand-query'
2
+ import schemaJson from 'SCHEMA_IMPORT';
3
+ import { getIn } from 'final-form';
4
+ import * as G from 'graphql';
5
+ import React from 'react';
6
+ import { useSearchParams } from 'react-router-dom';
8
7
  import {
8
+ Client,
9
+ Collection,
10
+ ErrorDialog,
9
11
  Form,
10
- TinaCMS,
12
+ FormOptions,
13
+ GlobalFormPlugin,
11
14
  NAMER,
12
- TinaSchema,
13
- useCMS,
14
- resolveField,
15
- Collection,
16
15
  Template,
16
+ TinaCMS,
17
17
  TinaField,
18
- Client,
19
- FormOptions,
20
- GlobalFormPlugin,
18
+ TinaSchema,
21
19
  TinaState,
22
- ErrorDialog,
23
- } from 'tinacms'
24
- import { createForm, createGlobalForm, FormifyCallback } from './build-form'
20
+ resolveField,
21
+ useCMS,
22
+ } from 'tinacms';
23
+ import { z } from 'zod';
24
+ import { FormifyCallback, createForm, createGlobalForm } from './build-form';
25
+ import { showErrorModal } from './errors';
26
+ import { expandQuery, isConnectionType, isNodeType } from './expand-query';
25
27
  import type {
26
- PostMessage,
27
28
  Payload,
28
- SystemInfo,
29
+ PostMessage,
29
30
  ResolvedDocument,
30
- } from './types'
31
- import { getFormAndFieldNameFromMetadata } from './util'
32
- import { useSearchParams } from 'react-router-dom'
33
- import { showErrorModal } from './errors'
31
+ SystemInfo,
32
+ } from './types';
33
+ import { getFormAndFieldNameFromMetadata } from './util';
34
34
 
35
35
  const sysSchema = z.object({
36
36
  breadcrumbs: z.array(z.string()),
@@ -50,14 +50,14 @@ const sysSchema = z.object({
50
50
  format: z.string().optional().nullable(),
51
51
  matches: z.string().optional().nullable(),
52
52
  }),
53
- })
53
+ });
54
54
 
55
55
  const documentSchema: z.ZodType<ResolvedDocument> = z.object({
56
56
  _internalValues: z.record(z.unknown()),
57
57
  _internalSys: sysSchema,
58
- })
58
+ });
59
59
 
60
- const astNode = schemaJson as G.DocumentNode
60
+ const astNode = schemaJson as G.DocumentNode;
61
61
  const astNodeWithMeta: G.DocumentNode = {
62
62
  ...astNode,
63
63
  definitions: astNode.definitions.map((def) => {
@@ -103,7 +103,7 @@ const astNodeWithMeta: G.DocumentNode = {
103
103
  },
104
104
  },
105
105
  ],
106
- }
106
+ };
107
107
  }
108
108
  if (def.kind === 'ObjectTypeDefinition') {
109
109
  return {
@@ -147,68 +147,68 @@ const astNodeWithMeta: G.DocumentNode = {
147
147
  },
148
148
  },
149
149
  ],
150
- }
150
+ };
151
151
  }
152
- return def
152
+ return def;
153
153
  }),
154
- }
155
- const schema = G.buildASTSchema(astNode)
156
- const schemaForResolver = G.buildASTSchema(astNodeWithMeta)
154
+ };
155
+ const schema = G.buildASTSchema(astNode);
156
+ const schemaForResolver = G.buildASTSchema(astNodeWithMeta);
157
157
 
158
158
  const isRejected = (
159
159
  input: PromiseSettledResult<unknown>
160
- ): input is PromiseRejectedResult => input.status === 'rejected'
160
+ ): input is PromiseRejectedResult => input.status === 'rejected';
161
161
 
162
162
  const isFulfilled = <T>(
163
163
  input: PromiseSettledResult<T>
164
- ): input is PromiseFulfilledResult<T> => input.status === 'fulfilled'
164
+ ): input is PromiseFulfilledResult<T> => input.status === 'fulfilled';
165
165
 
166
166
  export const useGraphQLReducer = (
167
167
  iframe: React.MutableRefObject<HTMLIFrameElement>,
168
168
  url: string
169
169
  ) => {
170
- const cms = useCMS()
171
- const tinaSchema = cms.api.tina.schema as TinaSchema
172
- const [payloads, setPayloads] = React.useState<Payload[]>([])
173
- const [requestErrors, setRequestErrors] = React.useState<string[]>([])
174
- const [searchParams, setSearchParams] = useSearchParams()
170
+ const cms = useCMS();
171
+ const tinaSchema = cms.api.tina.schema as TinaSchema;
172
+ const [payloads, setPayloads] = React.useState<Payload[]>([]);
173
+ const [requestErrors, setRequestErrors] = React.useState<string[]>([]);
174
+ const [searchParams, setSearchParams] = useSearchParams();
175
175
  const [results, setResults] = React.useState<
176
176
  {
177
- id: string
177
+ id: string;
178
178
  data:
179
179
  | {
180
- [key: string]: any
180
+ [key: string]: any;
181
181
  }
182
182
  | null
183
- | undefined
183
+ | undefined;
184
184
  }[]
185
- >([])
185
+ >([]);
186
186
  const [documentsToResolve, setDocumentsToResolve] = React.useState<string[]>(
187
187
  []
188
- )
188
+ );
189
189
  const [resolvedDocuments, setResolvedDocuments] = React.useState<
190
190
  ResolvedDocument[]
191
- >([])
192
- const [operationIndex, setOperationIndex] = React.useState(0)
191
+ >([]);
192
+ const [operationIndex, setOperationIndex] = React.useState(0);
193
193
 
194
- const activeField = searchParams.get('active-field')
194
+ const activeField = searchParams.get('active-field');
195
195
 
196
196
  React.useEffect(() => {
197
197
  const run = async () => {
198
198
  return Promise.all(
199
199
  documentsToResolve.map(async (documentId) => {
200
- return await getDocument(documentId, cms.api.tina)
200
+ return await getDocument(documentId, cms.api.tina);
201
201
  })
202
- )
203
- }
202
+ );
203
+ };
204
204
  if (documentsToResolve.length) {
205
205
  run().then((docs) => {
206
- setResolvedDocuments((resolvedDocs) => [...resolvedDocs, ...docs])
207
- setDocumentsToResolve([])
208
- setOperationIndex((i) => i + 1)
209
- })
206
+ setResolvedDocuments((resolvedDocs) => [...resolvedDocs, ...docs]);
207
+ setDocumentsToResolve([]);
208
+ setOperationIndex((i) => i + 1);
209
+ });
210
210
  }
211
- }, [documentsToResolve.join('.')])
211
+ }, [documentsToResolve.join('.')]);
212
212
 
213
213
  /**
214
214
  * Note: since React runs effects twice in development this will run twice for a given query
@@ -216,39 +216,41 @@ export const useGraphQLReducer = (
216
216
  */
217
217
  React.useEffect(() => {
218
218
  const run = async () => {
219
- setRequestErrors([])
219
+ setRequestErrors([]);
220
220
  // gather the errors and display an error message containing each error unique message
221
221
  return Promise.allSettled(
222
222
  payloads.map(async (payload) => {
223
223
  // This payload has already been expanded, skip it.
224
224
  if (payload.expandedQuery) {
225
- return payload
225
+ return payload;
226
226
  } else {
227
- const expandedPayload = await expandPayload(payload, cms)
228
- processPayload(expandedPayload)
229
- return expandedPayload
227
+ const expandedPayload = await expandPayload(payload, cms);
228
+ processPayload(expandedPayload);
229
+ return expandedPayload;
230
230
  }
231
231
  })
232
- )
233
- }
232
+ );
233
+ };
234
234
  if (payloads.length) {
235
235
  run().then((updatedPayloads) => {
236
- setPayloads(updatedPayloads.filter(isFulfilled).map((p) => p.value))
236
+ setPayloads(updatedPayloads.filter(isFulfilled).map((p) => p.value));
237
237
  setRequestErrors(
238
238
  updatedPayloads.filter(isRejected).map((p) => String(p.reason))
239
- )
240
- })
239
+ );
240
+ });
241
241
  }
242
- }, [JSON.stringify(payloads), cms])
242
+ }, [JSON.stringify(payloads), cms]);
243
243
 
244
244
  const processPayload = React.useCallback(
245
245
  (payload: Payload) => {
246
- const { expandedQueryForResolver, variables, expandedData } = payload
246
+ const { expandedQueryForResolver, variables, expandedData } = payload;
247
247
  if (!expandedQueryForResolver || !expandedData) {
248
- throw new Error(`Unable to process payload which has not been expanded`)
248
+ throw new Error(
249
+ `Unable to process payload which has not been expanded`
250
+ );
249
251
  }
250
- const formListItems: TinaState['formLists'][number]['items'] = []
251
- const formIds: string[] = []
252
+ const formListItems: TinaState['formLists'][number]['items'] = [];
253
+ const formIds: string[] = [];
252
254
 
253
255
  const result = G.graphqlSync({
254
256
  schema: schemaForResolver,
@@ -256,7 +258,7 @@ export const useGraphQLReducer = (
256
258
  variableValues: variables,
257
259
  rootValue: expandedData,
258
260
  fieldResolver: (source, args, context, info) => {
259
- const fieldName = info.fieldName
261
+ const fieldName = info.fieldName;
260
262
  /**
261
263
  * Since the `source` for this resolver is the query that
262
264
  * ran before passing it into `useTina`, we need to take aliases
@@ -265,35 +267,35 @@ export const useGraphQLReducer = (
265
267
  * solution as the `value` gets overwritten depending on the alias
266
268
  * query.
267
269
  */
268
- const aliases: string[] = []
270
+ const aliases: string[] = [];
269
271
  info.fieldNodes.forEach((fieldNode) => {
270
272
  if (fieldNode.alias) {
271
- aliases.push(fieldNode.alias.value)
273
+ aliases.push(fieldNode.alias.value);
272
274
  }
273
- })
274
- let value = source[fieldName] as unknown
275
+ });
276
+ let value = source[fieldName] as unknown;
275
277
  aliases.forEach((alias) => {
276
- const aliasValue = source[alias]
278
+ const aliasValue = source[alias];
277
279
  if (aliasValue) {
278
- value = aliasValue
280
+ value = aliasValue;
279
281
  }
280
- })
282
+ });
281
283
  if (fieldName === '_sys') {
282
- return source._internalSys
284
+ return source._internalSys;
283
285
  }
284
286
  if (fieldName === '_values') {
285
- return source._internalValues
287
+ return source._internalValues;
286
288
  }
287
289
  if (info.fieldName === '_content_source') {
288
- const pathArray = G.responsePathAsArray(info.path)
290
+ const pathArray = G.responsePathAsArray(info.path);
289
291
  return {
290
292
  queryId: payload.id,
291
293
  path: pathArray.slice(0, pathArray.length - 1),
292
- }
294
+ };
293
295
  }
294
296
  if (info.fieldName === '_tina_metadata') {
295
297
  if (value) {
296
- return value
298
+ return value;
297
299
  }
298
300
  // TODO: ensure all fields that have _tina_metadata
299
301
  // actually need it
@@ -301,77 +303,77 @@ export const useGraphQLReducer = (
301
303
  id: null,
302
304
  fields: [],
303
305
  prefix: '',
304
- }
306
+ };
305
307
  }
306
308
  if (isConnectionType(info.returnType)) {
307
- const name = G.getNamedType(info.returnType).name
309
+ const name = G.getNamedType(info.returnType).name;
308
310
  const connectionCollection = tinaSchema
309
311
  .getCollections()
310
312
  .find((collection) => {
311
313
  const collectionName = NAMER.referenceConnectionType(
312
314
  collection.namespace
313
- )
315
+ );
314
316
  if (collectionName === name) {
315
- return true
317
+ return true;
316
318
  }
317
- return false
318
- })
319
+ return false;
320
+ });
319
321
  if (connectionCollection) {
320
322
  formListItems.push({
321
323
  type: 'list',
322
324
  label: connectionCollection.label || connectionCollection.name,
323
- })
325
+ });
324
326
  }
325
327
  }
326
328
  if (isNodeType(info.returnType)) {
327
329
  if (!value) {
328
- return
330
+ return;
329
331
  }
330
- let resolvedDocument: ResolvedDocument
332
+ let resolvedDocument: ResolvedDocument;
331
333
  // This is a reference from another form
332
334
  if (typeof value === 'string') {
333
335
  const valueFromSetup = getIn(
334
336
  expandedData,
335
337
  G.responsePathAsArray(info.path).join('.')
336
- )
338
+ );
337
339
  const maybeResolvedDocument = resolvedDocuments.find(
338
340
  (doc) => doc._internalSys.path === value
339
- )
341
+ );
340
342
  // If we already have this document, use it.
341
343
  if (maybeResolvedDocument) {
342
- resolvedDocument = maybeResolvedDocument
344
+ resolvedDocument = maybeResolvedDocument;
343
345
  } else if (valueFromSetup) {
344
346
  // Else, even though in this context the value is a string because it's
345
347
  // resolved from a parent form, if the reference hasn't changed
346
348
  // from when we ran the setup query, we can avoid a data fetch
347
349
  // here and just grab it from the response
348
350
  const maybeResolvedDocument =
349
- documentSchema.parse(valueFromSetup)
351
+ documentSchema.parse(valueFromSetup);
350
352
  if (maybeResolvedDocument._internalSys.path === value) {
351
- resolvedDocument = maybeResolvedDocument
353
+ resolvedDocument = maybeResolvedDocument;
352
354
  } else {
353
- throw new NoFormError(`No form found`, value)
355
+ throw new NoFormError(`No form found`, value);
354
356
  }
355
357
  } else {
356
- throw new NoFormError(`No form found`, value)
358
+ throw new NoFormError(`No form found`, value);
357
359
  }
358
360
  } else {
359
- resolvedDocument = documentSchema.parse(value)
361
+ resolvedDocument = documentSchema.parse(value);
360
362
  }
361
- const id = resolvedDocument._internalSys.path
362
- formIds.push(id)
363
+ const id = resolvedDocument._internalSys.path;
364
+ formIds.push(id);
363
365
  const existingForm = cms.state.forms.find(
364
366
  (f) => f.tinaForm.id === id
365
- )
367
+ );
366
368
 
367
- const pathArray = G.responsePathAsArray(info.path)
368
- const pathString = pathArray.join('.')
369
+ const pathArray = G.responsePathAsArray(info.path);
370
+ const pathString = pathArray.join('.');
369
371
  const ancestors = formListItems.filter((item) => {
370
372
  if (item.type === 'document') {
371
- return pathString.startsWith(item.path)
373
+ return pathString.startsWith(item.path);
372
374
  }
373
- })
374
- const parent = ancestors[ancestors.length - 1]
375
+ });
376
+ const parent = ancestors[ancestors.length - 1];
375
377
  if (parent) {
376
378
  if (parent.type === 'document') {
377
379
  parent.subItems.push({
@@ -379,7 +381,7 @@ export const useGraphQLReducer = (
379
381
  path: pathString,
380
382
  formId: id,
381
383
  subItems: [],
382
- })
384
+ });
383
385
  }
384
386
  } else {
385
387
  formListItems.push({
@@ -387,7 +389,7 @@ export const useGraphQLReducer = (
387
389
  path: pathString,
388
390
  formId: id,
389
391
  subItems: [],
390
- })
392
+ });
391
393
  }
392
394
 
393
395
  if (!existingForm) {
@@ -396,65 +398,65 @@ export const useGraphQLReducer = (
396
398
  tinaSchema,
397
399
  payloadId: payload.id,
398
400
  cms,
399
- })
401
+ });
400
402
  form.subscribe(
401
403
  () => {
402
- setOperationIndex((i) => i + 1)
404
+ setOperationIndex((i) => i + 1);
403
405
  },
404
406
  { values: true }
405
- )
407
+ );
406
408
  return resolveDocument(
407
409
  resolvedDocument,
408
410
  template,
409
411
  form,
410
412
  pathString
411
- )
413
+ );
412
414
  } else {
413
- existingForm.tinaForm.addQuery(payload.id)
415
+ existingForm.tinaForm.addQuery(payload.id);
414
416
  const { template } = getTemplateForDocument(
415
417
  resolvedDocument,
416
418
  tinaSchema
417
- )
418
- existingForm.tinaForm.addQuery(payload.id)
419
+ );
420
+ existingForm.tinaForm.addQuery(payload.id);
419
421
  return resolveDocument(
420
422
  resolvedDocument,
421
423
  template,
422
424
  existingForm.tinaForm,
423
425
  pathString
424
- )
426
+ );
425
427
  }
426
428
  }
427
- return value
429
+ return value;
428
430
  },
429
- })
431
+ });
430
432
  if (result.errors) {
431
433
  result.errors.forEach((error) => {
432
434
  if (
433
435
  error instanceof G.GraphQLError &&
434
436
  error.originalError instanceof NoFormError
435
437
  ) {
436
- const id = error.originalError.id
438
+ const id = error.originalError.id;
437
439
  setDocumentsToResolve((docs) => [
438
440
  ...docs.filter((doc) => doc !== id),
439
441
  id,
440
- ])
442
+ ]);
441
443
  } else {
442
- console.log(error)
444
+ console.log(error);
443
445
  // throw new Error(
444
446
  // `Error processing value change, please contact support`
445
447
  // )
446
448
  }
447
- })
449
+ });
448
450
  } else {
449
451
  if (result.data) {
450
452
  setResults((results) => [
451
453
  ...results.filter((result) => result.id !== payload.id),
452
454
  { id: payload.id, data: result.data },
453
- ])
455
+ ]);
454
456
  }
455
457
  if (activeField) {
456
- setSearchParams({})
457
- const [queryId, eventFieldName] = activeField.split('---')
458
+ setSearchParams({});
459
+ const [queryId, eventFieldName] = activeField.split('---');
458
460
  if (queryId === payload.id) {
459
461
  if (result?.data) {
460
462
  cms.dispatch({
@@ -463,19 +465,19 @@ export const useGraphQLReducer = (
463
465
  result.data,
464
466
  eventFieldName
465
467
  ),
466
- })
468
+ });
467
469
  }
468
470
  cms.dispatch({
469
471
  type: 'sidebar:set-display-state',
470
472
  value: 'openOrFull',
471
- })
473
+ });
472
474
  }
473
475
  }
474
476
  iframe.current?.contentWindow?.postMessage({
475
477
  type: 'updateData',
476
478
  id: payload.id,
477
479
  data: result.data,
478
- })
480
+ });
479
481
  }
480
482
  cms.dispatch({
481
483
  type: 'form-lists:add',
@@ -485,13 +487,13 @@ export const useGraphQLReducer = (
485
487
  items: formListItems,
486
488
  formIds,
487
489
  },
488
- })
490
+ });
489
491
  },
490
492
  [
491
493
  resolvedDocuments.map((doc) => doc._internalSys.path).join('.'),
492
494
  activeField,
493
495
  ]
494
- )
496
+ );
495
497
 
496
498
  const handleMessage = React.useCallback(
497
499
  (event: MessageEvent<PostMessage>) => {
@@ -499,50 +501,50 @@ export const useGraphQLReducer = (
499
501
  cms.dispatch({
500
502
  type: 'forms:set-active-form-id',
501
503
  value: event.data.formId,
502
- })
504
+ });
503
505
  }
504
506
 
505
507
  if (event?.data?.type === 'quick-edit') {
506
508
  cms.dispatch({
507
509
  type: 'set-quick-editing-supported',
508
510
  value: event.data.value,
509
- })
511
+ });
510
512
  iframe.current?.contentWindow?.postMessage({
511
513
  type: 'quickEditEnabled',
512
514
  value: cms.state.sidebarDisplayState === 'open',
513
- })
515
+ });
514
516
  }
515
517
  if (event?.data?.type === 'isEditMode') {
516
518
  iframe?.current?.contentWindow?.postMessage({
517
519
  type: 'tina:editMode',
518
- })
520
+ });
519
521
  }
520
522
  if (event.data.type === 'field:selected') {
521
- const [queryId, eventFieldName] = event.data.fieldName.split('---')
522
- const result = results.find((res) => res.id === queryId)
523
+ const [queryId, eventFieldName] = event.data.fieldName.split('---');
524
+ const result = results.find((res) => res.id === queryId);
523
525
  if (result?.data) {
524
526
  cms.dispatch({
525
527
  type: 'forms:set-active-field-name',
526
528
  value: getFormAndFieldNameFromMetadata(result.data, eventFieldName),
527
- })
529
+ });
528
530
  }
529
531
  cms.dispatch({
530
532
  type: 'sidebar:set-display-state',
531
533
  value: 'openOrFull',
532
- })
534
+ });
533
535
  }
534
536
  if (event.data.type === 'close') {
535
- const payloadSchema = z.object({ id: z.string() })
536
- const { id } = payloadSchema.parse(event.data)
537
+ const payloadSchema = z.object({ id: z.string() });
538
+ const { id } = payloadSchema.parse(event.data);
537
539
  setPayloads((previous) =>
538
540
  previous.filter((payload) => payload.id !== id)
539
- )
540
- setResults((previous) => previous.filter((result) => result.id !== id))
541
+ );
542
+ setResults((previous) => previous.filter((result) => result.id !== id));
541
543
  cms.forms.all().map((form) => {
542
- form.removeQuery(id)
543
- })
544
- cms.removeOrphanedForms()
545
- cms.dispatch({ type: 'form-lists:remove', value: id })
544
+ form.removeQuery(id);
545
+ });
546
+ cms.removeOrphanedForms();
547
+ cms.dispatch({ type: 'form-lists:remove', value: id });
546
548
  }
547
549
  if (event.data.type === 'open') {
548
550
  const payloadSchema = z.object({
@@ -550,60 +552,66 @@ export const useGraphQLReducer = (
550
552
  query: z.string(),
551
553
  variables: z.record(z.unknown()),
552
554
  data: z.record(z.unknown()),
553
- })
554
- const payload = payloadSchema.parse(event.data)
555
+ });
556
+ const payload = payloadSchema.parse(event.data);
555
557
  setPayloads((payloads) => [
556
558
  ...payloads.filter(({ id }) => id !== payload.id),
557
559
  payload,
558
- ])
560
+ ]);
561
+ }
562
+ if (event.data.type === 'url-changed') {
563
+ cms.dispatch({
564
+ type: 'sidebar:set-loading-state',
565
+ value: true,
566
+ });
559
567
  }
560
568
  },
561
569
  [cms, JSON.stringify(results)]
562
- )
570
+ );
563
571
 
564
572
  React.useEffect(() => {
565
573
  payloads.forEach((payload) => {
566
574
  if (payload.expandedData) {
567
- processPayload(payload)
575
+ processPayload(payload);
568
576
  }
569
- })
570
- }, [operationIndex])
577
+ });
578
+ }, [operationIndex]);
571
579
 
572
580
  React.useEffect(() => {
573
581
  return () => {
574
- setPayloads([])
575
- setResults([])
576
- cms.removeAllForms()
577
- cms.dispatch({ type: 'form-lists:clear' })
578
- }
579
- }, [url])
582
+ setPayloads([]);
583
+ setResults([]);
584
+ cms.removeAllForms();
585
+ cms.dispatch({ type: 'form-lists:clear' });
586
+ };
587
+ }, [url]);
580
588
 
581
589
  React.useEffect(() => {
582
590
  iframe.current?.contentWindow?.postMessage({
583
591
  type: 'quickEditEnabled',
584
592
  value: cms.state.sidebarDisplayState === 'open',
585
- })
586
- }, [cms.state.sidebarDisplayState])
593
+ });
594
+ }, [cms.state.sidebarDisplayState]);
587
595
 
588
596
  React.useEffect(() => {
589
- cms.dispatch({ type: 'set-edit-mode', value: 'visual' })
597
+ cms.dispatch({ type: 'set-edit-mode', value: 'visual' });
590
598
  if (iframe) {
591
- window.addEventListener('message', handleMessage)
599
+ window.addEventListener('message', handleMessage);
592
600
  }
593
601
 
594
602
  return () => {
595
- window.removeEventListener('message', handleMessage)
596
- cms.removeAllForms()
597
- cms.dispatch({ type: 'set-edit-mode', value: 'basic' })
598
- }
599
- }, [iframe.current, JSON.stringify(results)])
603
+ window.removeEventListener('message', handleMessage);
604
+ cms.removeAllForms();
605
+ cms.dispatch({ type: 'set-edit-mode', value: 'basic' });
606
+ };
607
+ }, [iframe.current, JSON.stringify(results)]);
600
608
 
601
609
  React.useEffect(() => {
602
610
  if (requestErrors.length) {
603
- showErrorModal('Unexpected error querying content', requestErrors, cms)
611
+ showErrorModal('Unexpected error querying content', requestErrors, cms);
604
612
  }
605
- }, [requestErrors])
606
- }
613
+ }, [requestErrors]);
614
+ };
607
615
 
608
616
  const onSubmit = async (
609
617
  collection: Collection<true>,
@@ -611,7 +619,7 @@ const onSubmit = async (
611
619
  payload: Record<string, unknown>,
612
620
  cms: TinaCMS
613
621
  ) => {
614
- const tinaSchema = cms.api.tina.schema
622
+ const tinaSchema = cms.api.tina.schema;
615
623
  try {
616
624
  const mutationString = `#graphql
617
625
  mutation UpdateDocument($collection: String!, $relativePath: String!, $params: DocumentUpdateMutation!) {
@@ -619,7 +627,7 @@ const onSubmit = async (
619
627
  __typename
620
628
  }
621
629
  }
622
- `
630
+ `;
623
631
 
624
632
  await cms.api.tina.request(mutationString, {
625
633
  variables: {
@@ -627,8 +635,8 @@ const onSubmit = async (
627
635
  relativePath: relativePath,
628
636
  params: tinaSchema.transformPayload(collection.name, payload),
629
637
  },
630
- })
631
- cms.alerts.success('Document saved!')
638
+ });
639
+ cms.alerts.success('Document saved!');
632
640
  } catch (e) {
633
641
  cms.alerts.error(() =>
634
642
  ErrorDialog({
@@ -636,12 +644,12 @@ const onSubmit = async (
636
644
  message: 'Tina caught an error while updating the page',
637
645
  error: e,
638
646
  })
639
- )
640
- console.error(e)
647
+ );
648
+ console.error(e);
641
649
  }
642
- }
650
+ };
643
651
 
644
- type Path = (string | number)[]
652
+ type Path = (string | number)[];
645
653
 
646
654
  const resolveDocument = (
647
655
  doc: ResolvedDocument,
@@ -650,20 +658,20 @@ const resolveDocument = (
650
658
  pathToDocument: string
651
659
  ): ResolvedDocument => {
652
660
  // @ts-ignore AnyField and TinaField don't mix
653
- const fields = form.fields as TinaField<true>[]
654
- const id = doc._internalSys.path
655
- const path: Path = []
661
+ const fields = form.fields as TinaField<true>[];
662
+ const id = doc._internalSys.path;
663
+ const path: Path = [];
656
664
  const formValues = resolveFormValue({
657
665
  fields: fields,
658
666
  values: form.values,
659
667
  path,
660
668
  id,
661
669
  pathToDocument,
662
- })
663
- const metadataFields: Record<string, string> = {}
670
+ });
671
+ const metadataFields: Record<string, string> = {};
664
672
  Object.keys(formValues).forEach((key) => {
665
- metadataFields[key] = [...path, key].join('.')
666
- })
673
+ metadataFields[key] = [...path, key].join('.');
674
+ });
667
675
 
668
676
  return {
669
677
  ...formValues,
@@ -679,8 +687,8 @@ const resolveDocument = (
679
687
  _internalSys: doc._internalSys,
680
688
  _internalValues: doc._internalValues,
681
689
  __typename: NAMER.dataTypeName(template.namespace),
682
- }
683
- }
690
+ };
691
+ };
684
692
 
685
693
  const resolveFormValue = <T extends Record<string, unknown>>({
686
694
  fields,
@@ -690,21 +698,21 @@ const resolveFormValue = <T extends Record<string, unknown>>({
690
698
  pathToDocument,
691
699
  }: // tinaSchema,
692
700
  {
693
- fields: TinaField<true>[]
694
- values: T
695
- path: Path
696
- id: string
697
- pathToDocument: string
701
+ fields: TinaField<true>[];
702
+ values: T;
703
+ path: Path;
704
+ id: string;
705
+ pathToDocument: string;
698
706
  // tinaSchema: TinaSchema
699
707
  }): T & { __typename?: string } => {
700
- const accum: Record<string, unknown> = {}
708
+ const accum: Record<string, unknown> = {};
701
709
  fields.forEach((field) => {
702
- const v = values[field.name]
710
+ const v = values[field.name];
703
711
  if (typeof v === 'undefined') {
704
- return
712
+ return;
705
713
  }
706
714
  if (v === null) {
707
- return
715
+ return;
708
716
  }
709
717
  accum[field.name] = resolveFieldValue({
710
718
  field,
@@ -712,10 +720,10 @@ const resolveFormValue = <T extends Record<string, unknown>>({
712
720
  path,
713
721
  id,
714
722
  pathToDocument,
715
- })
716
- })
717
- return accum as T & { __typename?: string }
718
- }
723
+ });
724
+ });
725
+ return accum as T & { __typename?: string };
726
+ };
719
727
  const resolveFieldValue = ({
720
728
  field,
721
729
  value,
@@ -723,11 +731,11 @@ const resolveFieldValue = ({
723
731
  id,
724
732
  pathToDocument,
725
733
  }: {
726
- field: TinaField<true>
727
- value: unknown
728
- path: Path
729
- id: string
730
- pathToDocument: string
734
+ field: TinaField<true>;
735
+ value: unknown;
736
+ path: Path;
737
+ id: string;
738
+ pathToDocument: string;
731
739
  }) => {
732
740
  switch (field.type) {
733
741
  case 'object': {
@@ -735,15 +743,17 @@ const resolveFieldValue = ({
735
743
  if (field.list) {
736
744
  if (Array.isArray(value)) {
737
745
  return value.map((item, index) => {
738
- const template = field.templates[item._template]
746
+ const template = field.templates[item._template];
739
747
  if (typeof template === 'string') {
740
- throw new Error('Global templates not supported')
748
+ throw new Error('Global templates not supported');
741
749
  }
742
- const nextPath = [...path, field.name, index]
743
- const metadataFields: Record<string, string> = {}
750
+ const nextPath = [...path, field.name, index];
751
+ const metadataFields: Record<string, string> = {};
744
752
  template.fields.forEach((field) => {
745
- metadataFields[field.name] = [...nextPath, field.name].join('.')
746
- })
753
+ metadataFields[field.name] = [...nextPath, field.name].join(
754
+ '.'
755
+ );
756
+ });
747
757
  return {
748
758
  __typename: NAMER.dataTypeName(template.namespace),
749
759
  _tina_metadata: {
@@ -759,29 +769,29 @@ const resolveFieldValue = ({
759
769
  id,
760
770
  pathToDocument,
761
771
  }),
762
- }
763
- })
772
+ };
773
+ });
764
774
  }
765
775
  } else {
766
776
  // not implemented
767
777
  }
768
778
  }
769
779
 
770
- const templateFields = field.fields
780
+ const templateFields = field.fields;
771
781
  if (typeof templateFields === 'string') {
772
- throw new Error('Global templates not supported')
782
+ throw new Error('Global templates not supported');
773
783
  }
774
784
  if (!templateFields) {
775
- throw new Error(`Expected to find sub-fields on field ${field.name}`)
785
+ throw new Error(`Expected to find sub-fields on field ${field.name}`);
776
786
  }
777
787
  if (field.list) {
778
788
  if (Array.isArray(value)) {
779
789
  return value.map((item, index) => {
780
- const nextPath = [...path, field.name, index]
781
- const metadataFields: Record<string, string> = {}
790
+ const nextPath = [...path, field.name, index];
791
+ const metadataFields: Record<string, string> = {};
782
792
  templateFields.forEach((field) => {
783
- metadataFields[field.name] = [...nextPath, field.name].join('.')
784
- })
793
+ metadataFields[field.name] = [...nextPath, field.name].join('.');
794
+ });
785
795
  return {
786
796
  __typename: NAMER.dataTypeName(field.namespace),
787
797
  _tina_metadata: {
@@ -797,15 +807,15 @@ const resolveFieldValue = ({
797
807
  id,
798
808
  pathToDocument,
799
809
  }),
800
- }
801
- })
810
+ };
811
+ });
802
812
  }
803
813
  } else {
804
- const nextPath = [...path, field.name]
805
- const metadataFields: Record<string, string> = {}
814
+ const nextPath = [...path, field.name];
815
+ const metadataFields: Record<string, string> = {};
806
816
  templateFields.forEach((field) => {
807
- metadataFields[field.name] = [...nextPath, field.name].join('.')
808
- })
817
+ metadataFields[field.name] = [...nextPath, field.name].join('.');
818
+ });
809
819
  return {
810
820
  __typename: NAMER.dataTypeName(field.namespace),
811
821
  _tina_metadata: {
@@ -821,18 +831,21 @@ const resolveFieldValue = ({
821
831
  id,
822
832
  pathToDocument,
823
833
  }),
824
- }
834
+ };
825
835
  }
826
836
  }
827
837
  default: {
828
- return value
838
+ return value;
829
839
  }
830
840
  }
831
- }
841
+ };
832
842
 
833
843
  const getDocument = async (id: string, tina: Client) => {
834
844
  const response = await tina.request<{
835
- node: { _internalSys: SystemInfo; _internalValues: Record<string, unknown> }
845
+ node: {
846
+ _internalSys: SystemInfo;
847
+ _internalValues: Record<string, unknown>;
848
+ };
836
849
  }>(
837
850
  `query GetNode($id: String!) {
838
851
  node(id: $id) {
@@ -865,29 +878,29 @@ _internalSys: _sys {
865
878
  }
866
879
  }`,
867
880
  { variables: { id: id } }
868
- )
869
- return response.node
870
- }
881
+ );
882
+ return response.node;
883
+ };
871
884
 
872
885
  const expandPayload = async (
873
886
  payload: Payload,
874
887
  cms: TinaCMS
875
888
  ): Promise<Payload> => {
876
- const { query, variables } = payload
877
- const documentNode = G.parse(query)
878
- const expandedDocumentNode = expandQuery({ schema, documentNode })
879
- const expandedQuery = G.print(expandedDocumentNode)
889
+ const { query, variables } = payload;
890
+ const documentNode = G.parse(query);
891
+ const expandedDocumentNode = expandQuery({ schema, documentNode });
892
+ const expandedQuery = G.print(expandedDocumentNode);
880
893
  const expandedData = await cms.api.tina.request<object>(expandedQuery, {
881
894
  variables,
882
- })
895
+ });
883
896
 
884
897
  const expandedDocumentNodeForResolver = expandQuery({
885
898
  schema: schemaForResolver,
886
899
  documentNode,
887
- })
888
- const expandedQueryForResolver = G.print(expandedDocumentNodeForResolver)
889
- return { ...payload, expandedQuery, expandedData, expandedQueryForResolver }
890
- }
900
+ });
901
+ const expandedQueryForResolver = G.print(expandedDocumentNodeForResolver);
902
+ return { ...payload, expandedQuery, expandedData, expandedQueryForResolver };
903
+ };
891
904
 
892
905
  /**
893
906
  * When we resolve the graphql data we check for these errors,
@@ -895,11 +908,11 @@ const expandPayload = async (
895
908
  * process it once we have that document
896
909
  */
897
910
  class NoFormError extends Error {
898
- id: string
911
+ id: string;
899
912
  constructor(msg: string, id: string) {
900
- super(msg)
901
- this.id = id
902
- Object.setPrototypeOf(this, NoFormError.prototype)
913
+ super(msg);
914
+ this.id = id;
915
+ Object.setPrototypeOf(this, NoFormError.prototype);
903
916
  }
904
917
  }
905
918
 
@@ -907,22 +920,22 @@ const getTemplateForDocument = (
907
920
  resolvedDocument: ResolvedDocument,
908
921
  tinaSchema: TinaSchema
909
922
  ) => {
910
- const id = resolvedDocument._internalSys.path
911
- let collection: Collection<true> | undefined
923
+ const id = resolvedDocument._internalSys.path;
924
+ let collection: Collection<true> | undefined;
912
925
  try {
913
- collection = tinaSchema.getCollectionByFullPath(id)
926
+ collection = tinaSchema.getCollectionByFullPath(id);
914
927
  } catch (e) {}
915
928
 
916
929
  if (!collection) {
917
- throw new Error(`Unable to determine collection for path ${id}`)
930
+ throw new Error(`Unable to determine collection for path ${id}`);
918
931
  }
919
932
 
920
933
  const template = tinaSchema.getTemplateForData({
921
934
  data: resolvedDocument._internalValues,
922
935
  collection,
923
- })
924
- return { template, collection }
925
- }
936
+ });
937
+ return { template, collection };
938
+ };
926
939
 
927
940
  const buildForm = ({
928
941
  resolvedDocument,
@@ -930,18 +943,18 @@ const buildForm = ({
930
943
  payloadId,
931
944
  cms,
932
945
  }: {
933
- resolvedDocument: ResolvedDocument
934
- tinaSchema: TinaSchema
935
- payloadId: string
936
- cms: TinaCMS
946
+ resolvedDocument: ResolvedDocument;
947
+ tinaSchema: TinaSchema;
948
+ payloadId: string;
949
+ cms: TinaCMS;
937
950
  }) => {
938
951
  const { template, collection } = getTemplateForDocument(
939
952
  resolvedDocument,
940
953
  tinaSchema
941
- )
942
- const id = resolvedDocument._internalSys.path
943
- let form: Form | undefined
944
- let shouldRegisterForm = true
954
+ );
955
+ const id = resolvedDocument._internalSys.path;
956
+ let form: Form | undefined;
957
+ let shouldRegisterForm = true;
945
958
  const formConfig: FormOptions<any> = {
946
959
  id,
947
960
  initialValues: resolvedDocument._internalValues,
@@ -954,10 +967,10 @@ const buildForm = ({
954
967
  cms
955
968
  ),
956
969
  label: collection.label || collection.name,
957
- }
970
+ };
958
971
  if (tinaSchema.config.config?.formifyCallback) {
959
972
  const callback = tinaSchema.config.config
960
- ?.formifyCallback as FormifyCallback
973
+ ?.formifyCallback as FormifyCallback;
961
974
  form =
962
975
  callback(
963
976
  {
@@ -967,30 +980,30 @@ const buildForm = ({
967
980
  formConfig,
968
981
  },
969
982
  cms
970
- ) || undefined
983
+ ) || undefined;
971
984
  if (!form) {
972
985
  // If the form isn't created from formify, we still
973
986
  // need it, just don't show it to the user.
974
- shouldRegisterForm = false
975
- form = new Form(formConfig)
987
+ shouldRegisterForm = false;
988
+ form = new Form(formConfig);
976
989
  }
977
990
  } else {
978
991
  if (collection.ui?.global) {
979
- form = createGlobalForm(formConfig)
992
+ form = createGlobalForm(formConfig);
980
993
  } else {
981
- form = createForm(formConfig)
994
+ form = createForm(formConfig);
982
995
  }
983
996
  }
984
997
  if (form) {
985
998
  if (shouldRegisterForm) {
986
999
  if (collection.ui?.global) {
987
- cms.plugins.add(new GlobalFormPlugin(form))
1000
+ cms.plugins.add(new GlobalFormPlugin(form));
988
1001
  }
989
- cms.dispatch({ type: 'forms:add', value: form })
1002
+ cms.dispatch({ type: 'forms:add', value: form });
990
1003
  }
991
1004
  }
992
1005
  if (!form) {
993
- throw new Error(`No form registered for ${id}.`)
1006
+ throw new Error(`No form registered for ${id}.`);
994
1007
  }
995
- return { template, form }
996
- }
1008
+ return { template, form };
1009
+ };