@tinacms/app 1.2.12 → 1.2.13
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/CHANGELOG.md +12 -0
- package/package.json +3 -3
- package/src/App.tsx +0 -5
- package/src/fields/rich-text/monaco/index.tsx +1 -1
- package/src/lib/expand-query.ts +38 -1
- package/src/lib/graphql-reducer.ts +253 -56
- package/src/lib/types.ts +8 -5
- package/src/lib/util.ts +129 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @tinacms/app
|
|
2
2
|
|
|
3
|
+
## 1.2.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7f95c1ce5: Reorganize the way fields are presented on the form to allow for deep-linking
|
|
8
|
+
- Updated dependencies [5a6018916]
|
|
9
|
+
- Updated dependencies [63dd98904]
|
|
10
|
+
- Updated dependencies [b3d98d159]
|
|
11
|
+
- Updated dependencies [7f95c1ce5]
|
|
12
|
+
- tinacms@1.5.6
|
|
13
|
+
- @tinacms/toolkit@1.7.2
|
|
14
|
+
|
|
3
15
|
## 1.2.12
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinacms/app",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.13",
|
|
4
4
|
"main": "src/main.tsx",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"devDependencies": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"@heroicons/react": "1.0.6",
|
|
15
15
|
"@monaco-editor/react": "4.4.5",
|
|
16
16
|
"@tinacms/mdx": "1.3.10",
|
|
17
|
-
"@tinacms/toolkit": "1.7.
|
|
17
|
+
"@tinacms/toolkit": "1.7.2",
|
|
18
18
|
"@xstate/react": "3.0.0",
|
|
19
19
|
"final-form": "4.20.7",
|
|
20
20
|
"graphiql": "^2.4.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"react-is": "17.0.2",
|
|
28
28
|
"react-router-dom": "6.3.0",
|
|
29
29
|
"tailwindcss": "^3.2.7",
|
|
30
|
-
"tinacms": "1.5.
|
|
30
|
+
"tinacms": "1.5.6",
|
|
31
31
|
"typescript": "^4.6.4",
|
|
32
32
|
"zod": "^3.14.3"
|
|
33
33
|
}
|
package/src/App.tsx
CHANGED
|
@@ -160,7 +160,7 @@ export const RawEditor = (props: RichTextType) => {
|
|
|
160
160
|
|
|
161
161
|
return (
|
|
162
162
|
<div className="relative">
|
|
163
|
-
<div className="sticky
|
|
163
|
+
<div className="sticky top-1 w-full flex justify-between mb-2 z-50 max-w-full">
|
|
164
164
|
<Button onClick={() => props.setRawMode(false)}>
|
|
165
165
|
View in rich-text editor
|
|
166
166
|
</Button>
|
package/src/lib/expand-query.ts
CHANGED
|
@@ -67,6 +67,13 @@ const addTypenameToDocument = (doc: G.DocumentNode) => {
|
|
|
67
67
|
})
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
const CONTENT_SOURCE_FIELD: G.FieldNode = {
|
|
71
|
+
kind: G.Kind.FIELD,
|
|
72
|
+
name: {
|
|
73
|
+
kind: G.Kind.NAME,
|
|
74
|
+
value: '_content_source',
|
|
75
|
+
},
|
|
76
|
+
}
|
|
70
77
|
const METADATA_FIELD: G.FieldNode = {
|
|
71
78
|
kind: G.Kind.FIELD,
|
|
72
79
|
name: {
|
|
@@ -94,7 +101,11 @@ const addMetadataField = (
|
|
|
94
101
|
selections: [],
|
|
95
102
|
}),
|
|
96
103
|
selections:
|
|
97
|
-
[
|
|
104
|
+
[
|
|
105
|
+
...(node.selectionSet?.selections || []),
|
|
106
|
+
METADATA_FIELD,
|
|
107
|
+
CONTENT_SOURCE_FIELD,
|
|
108
|
+
] || [],
|
|
98
109
|
},
|
|
99
110
|
}
|
|
100
111
|
}
|
|
@@ -229,3 +240,29 @@ export const isNodeType = (type: G.GraphQLOutputType) => {
|
|
|
229
240
|
}
|
|
230
241
|
}
|
|
231
242
|
}
|
|
243
|
+
|
|
244
|
+
export const isConnectionType = (type: G.GraphQLOutputType) => {
|
|
245
|
+
const namedType = G.getNamedType(type)
|
|
246
|
+
if (G.isInterfaceType(namedType)) {
|
|
247
|
+
if (namedType.name === 'Connection') {
|
|
248
|
+
return true
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (G.isUnionType(namedType)) {
|
|
252
|
+
const types = namedType.getTypes()
|
|
253
|
+
if (
|
|
254
|
+
types.every((type) => {
|
|
255
|
+
return type.getInterfaces().some((intfc) => intfc.name === 'Connection')
|
|
256
|
+
})
|
|
257
|
+
) {
|
|
258
|
+
return true
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (G.isObjectType(namedType)) {
|
|
262
|
+
if (
|
|
263
|
+
namedType.getInterfaces().some((intfc) => intfc.name === 'Connection')
|
|
264
|
+
) {
|
|
265
|
+
return true
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import * as G from 'graphql'
|
|
3
3
|
import { getIn } from 'final-form'
|
|
4
|
-
import { z
|
|
4
|
+
import { z } from 'zod'
|
|
5
5
|
// @ts-expect-error
|
|
6
6
|
import schemaJson from 'SCHEMA_IMPORT'
|
|
7
|
-
import { expandQuery, isNodeType } from './expand-query'
|
|
7
|
+
import { expandQuery, isConnectionType, isNodeType } from './expand-query'
|
|
8
8
|
import {
|
|
9
9
|
Form,
|
|
10
10
|
TinaCMS,
|
|
@@ -18,15 +18,17 @@ import {
|
|
|
18
18
|
Client,
|
|
19
19
|
FormOptions,
|
|
20
20
|
GlobalFormPlugin,
|
|
21
|
+
TinaState,
|
|
21
22
|
} from 'tinacms'
|
|
22
23
|
import { createForm, createGlobalForm, FormifyCallback } from './build-form'
|
|
23
24
|
import type {
|
|
24
25
|
PostMessage,
|
|
25
26
|
Payload,
|
|
26
27
|
SystemInfo,
|
|
27
|
-
Document,
|
|
28
28
|
ResolvedDocument,
|
|
29
29
|
} from './types'
|
|
30
|
+
import { getFormAndFieldNameFromMetadata } from './util'
|
|
31
|
+
import { useSearchParams } from 'react-router-dom'
|
|
30
32
|
|
|
31
33
|
const sysSchema = z.object({
|
|
32
34
|
breadcrumbs: z.array(z.string()),
|
|
@@ -56,6 +58,50 @@ const astNode = schemaJson as G.DocumentNode
|
|
|
56
58
|
const astNodeWithMeta: G.DocumentNode = {
|
|
57
59
|
...astNode,
|
|
58
60
|
definitions: astNode.definitions.map((def) => {
|
|
61
|
+
if (def.kind === 'InterfaceTypeDefinition') {
|
|
62
|
+
return {
|
|
63
|
+
...def,
|
|
64
|
+
fields: [
|
|
65
|
+
...(def.fields || []),
|
|
66
|
+
{
|
|
67
|
+
kind: 'FieldDefinition',
|
|
68
|
+
name: {
|
|
69
|
+
kind: 'Name',
|
|
70
|
+
value: '_tina_metadata',
|
|
71
|
+
},
|
|
72
|
+
arguments: [],
|
|
73
|
+
type: {
|
|
74
|
+
kind: 'NonNullType',
|
|
75
|
+
type: {
|
|
76
|
+
kind: 'NamedType',
|
|
77
|
+
name: {
|
|
78
|
+
kind: 'Name',
|
|
79
|
+
value: 'JSON',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
kind: 'FieldDefinition',
|
|
86
|
+
name: {
|
|
87
|
+
kind: 'Name',
|
|
88
|
+
value: '_content_source',
|
|
89
|
+
},
|
|
90
|
+
arguments: [],
|
|
91
|
+
type: {
|
|
92
|
+
kind: 'NonNullType',
|
|
93
|
+
type: {
|
|
94
|
+
kind: 'NamedType',
|
|
95
|
+
name: {
|
|
96
|
+
kind: 'Name',
|
|
97
|
+
value: 'JSON',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
}
|
|
104
|
+
}
|
|
59
105
|
if (def.kind === 'ObjectTypeDefinition') {
|
|
60
106
|
return {
|
|
61
107
|
...def,
|
|
@@ -79,6 +125,24 @@ const astNodeWithMeta: G.DocumentNode = {
|
|
|
79
125
|
},
|
|
80
126
|
},
|
|
81
127
|
},
|
|
128
|
+
{
|
|
129
|
+
kind: 'FieldDefinition',
|
|
130
|
+
name: {
|
|
131
|
+
kind: 'Name',
|
|
132
|
+
value: '_content_source',
|
|
133
|
+
},
|
|
134
|
+
arguments: [],
|
|
135
|
+
type: {
|
|
136
|
+
kind: 'NonNullType',
|
|
137
|
+
type: {
|
|
138
|
+
kind: 'NamedType',
|
|
139
|
+
name: {
|
|
140
|
+
kind: 'Name',
|
|
141
|
+
value: 'JSON',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
82
146
|
],
|
|
83
147
|
}
|
|
84
148
|
}
|
|
@@ -95,6 +159,18 @@ export const useGraphQLReducer = (
|
|
|
95
159
|
const cms = useCMS()
|
|
96
160
|
const tinaSchema = cms.api.tina.schema as TinaSchema
|
|
97
161
|
const [payloads, setPayloads] = React.useState<Payload[]>([])
|
|
162
|
+
const [searchParams, setSearchParams] = useSearchParams()
|
|
163
|
+
const [results, setResults] = React.useState<
|
|
164
|
+
{
|
|
165
|
+
id: string
|
|
166
|
+
data:
|
|
167
|
+
| {
|
|
168
|
+
[key: string]: any
|
|
169
|
+
}
|
|
170
|
+
| null
|
|
171
|
+
| undefined
|
|
172
|
+
}[]
|
|
173
|
+
>([])
|
|
98
174
|
const [documentsToResolve, setDocumentsToResolve] = React.useState<string[]>(
|
|
99
175
|
[]
|
|
100
176
|
)
|
|
@@ -144,7 +220,7 @@ export const useGraphQLReducer = (
|
|
|
144
220
|
setPayloads(updatedPayloads)
|
|
145
221
|
})
|
|
146
222
|
}
|
|
147
|
-
}, [
|
|
223
|
+
}, [JSON.stringify(payloads), cms])
|
|
148
224
|
|
|
149
225
|
const processPayload = React.useCallback(
|
|
150
226
|
(payload: Payload) => {
|
|
@@ -152,6 +228,8 @@ export const useGraphQLReducer = (
|
|
|
152
228
|
if (!expandedQueryForResolver || !expandedData) {
|
|
153
229
|
throw new Error(`Unable to process payload which has not been expanded`)
|
|
154
230
|
}
|
|
231
|
+
const formListItems: TinaState['formLists'][number]['items'] = []
|
|
232
|
+
const formIds: string[] = []
|
|
155
233
|
|
|
156
234
|
const result = G.graphqlSync({
|
|
157
235
|
schema: schemaForResolver,
|
|
@@ -189,6 +267,13 @@ export const useGraphQLReducer = (
|
|
|
189
267
|
if (fieldName === '_values') {
|
|
190
268
|
return source._internalValues
|
|
191
269
|
}
|
|
270
|
+
if (info.fieldName === '_content_source') {
|
|
271
|
+
const pathArray = G.responsePathAsArray(info.path)
|
|
272
|
+
return {
|
|
273
|
+
queryId: payload.id,
|
|
274
|
+
path: pathArray.slice(0, pathArray.length - 1),
|
|
275
|
+
}
|
|
276
|
+
}
|
|
192
277
|
if (info.fieldName === '_tina_metadata') {
|
|
193
278
|
if (value) {
|
|
194
279
|
return value
|
|
@@ -198,6 +283,27 @@ export const useGraphQLReducer = (
|
|
|
198
283
|
return {
|
|
199
284
|
id: null,
|
|
200
285
|
fields: [],
|
|
286
|
+
prefix: '',
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (isConnectionType(info.returnType)) {
|
|
290
|
+
const name = G.getNamedType(info.returnType).name
|
|
291
|
+
const connectionCollection = tinaSchema
|
|
292
|
+
.getCollections()
|
|
293
|
+
.find((collection) => {
|
|
294
|
+
const collectionName = NAMER.referenceConnectionType(
|
|
295
|
+
collection.namespace
|
|
296
|
+
)
|
|
297
|
+
if (collectionName === name) {
|
|
298
|
+
return true
|
|
299
|
+
}
|
|
300
|
+
return false
|
|
301
|
+
})
|
|
302
|
+
if (connectionCollection) {
|
|
303
|
+
formListItems.push({
|
|
304
|
+
type: 'list',
|
|
305
|
+
label: connectionCollection.label || connectionCollection.name,
|
|
306
|
+
})
|
|
201
307
|
}
|
|
202
308
|
}
|
|
203
309
|
if (isNodeType(info.returnType)) {
|
|
@@ -236,19 +342,37 @@ export const useGraphQLReducer = (
|
|
|
236
342
|
resolvedDocument = documentSchema.parse(value)
|
|
237
343
|
}
|
|
238
344
|
const id = resolvedDocument._internalSys.path
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
345
|
+
formIds.push(id)
|
|
346
|
+
const existingForm = cms.state.forms.find(
|
|
347
|
+
(f) => f.tinaForm.id === id
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
const pathArray = G.responsePathAsArray(info.path)
|
|
351
|
+
const pathString = pathArray.join('.')
|
|
352
|
+
const ancestors = formListItems.filter((item) => {
|
|
353
|
+
if (item.type === 'document') {
|
|
354
|
+
return pathString.startsWith(item.path)
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
const parent = ancestors[ancestors.length - 1]
|
|
358
|
+
if (parent) {
|
|
359
|
+
if (parent.type === 'document') {
|
|
360
|
+
parent.subItems.push({
|
|
361
|
+
type: 'document',
|
|
362
|
+
path: pathString,
|
|
363
|
+
formId: id,
|
|
364
|
+
subItems: [],
|
|
250
365
|
})
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
formListItems.push({
|
|
369
|
+
type: 'document',
|
|
370
|
+
path: pathString,
|
|
371
|
+
formId: id,
|
|
372
|
+
subItems: [],
|
|
373
|
+
})
|
|
251
374
|
}
|
|
375
|
+
|
|
252
376
|
if (!existingForm) {
|
|
253
377
|
const { form, template } = buildForm({
|
|
254
378
|
resolvedDocument,
|
|
@@ -262,15 +386,25 @@ export const useGraphQLReducer = (
|
|
|
262
386
|
},
|
|
263
387
|
{ values: true }
|
|
264
388
|
)
|
|
265
|
-
return resolveDocument(
|
|
389
|
+
return resolveDocument(
|
|
390
|
+
resolvedDocument,
|
|
391
|
+
template,
|
|
392
|
+
form,
|
|
393
|
+
pathString
|
|
394
|
+
)
|
|
266
395
|
} else {
|
|
267
|
-
existingForm.addQuery(payload.id)
|
|
396
|
+
existingForm.tinaForm.addQuery(payload.id)
|
|
268
397
|
const { template } = getTemplateForDocument(
|
|
269
398
|
resolvedDocument,
|
|
270
399
|
tinaSchema
|
|
271
400
|
)
|
|
272
|
-
existingForm.addQuery(payload.id)
|
|
273
|
-
return resolveDocument(
|
|
401
|
+
existingForm.tinaForm.addQuery(payload.id)
|
|
402
|
+
return resolveDocument(
|
|
403
|
+
resolvedDocument,
|
|
404
|
+
template,
|
|
405
|
+
existingForm.tinaForm,
|
|
406
|
+
pathString
|
|
407
|
+
)
|
|
274
408
|
}
|
|
275
409
|
}
|
|
276
410
|
return value
|
|
@@ -295,56 +429,94 @@ export const useGraphQLReducer = (
|
|
|
295
429
|
}
|
|
296
430
|
})
|
|
297
431
|
} else {
|
|
432
|
+
if (result.data) {
|
|
433
|
+
setResults((results) => [
|
|
434
|
+
...results.filter((result) => result.id !== payload.id),
|
|
435
|
+
{ id: payload.id, data: result.data },
|
|
436
|
+
])
|
|
437
|
+
}
|
|
438
|
+
const activeField = searchParams.get('active-field')
|
|
439
|
+
if (activeField) {
|
|
440
|
+
setSearchParams({})
|
|
441
|
+
const [queryId, eventFieldName] = activeField.split('---')
|
|
442
|
+
if (queryId === payload.id) {
|
|
443
|
+
if (result?.data) {
|
|
444
|
+
cms.dispatch({
|
|
445
|
+
type: 'forms:set-active-field-name',
|
|
446
|
+
value: getFormAndFieldNameFromMetadata(
|
|
447
|
+
result.data,
|
|
448
|
+
eventFieldName
|
|
449
|
+
),
|
|
450
|
+
})
|
|
451
|
+
}
|
|
452
|
+
cms.dispatch({
|
|
453
|
+
type: 'sidebar:set-display-state',
|
|
454
|
+
value: 'openOrFull',
|
|
455
|
+
})
|
|
456
|
+
}
|
|
457
|
+
}
|
|
298
458
|
iframe.current?.contentWindow?.postMessage({
|
|
299
459
|
type: 'updateData',
|
|
300
460
|
id: payload.id,
|
|
301
461
|
data: result.data,
|
|
302
462
|
})
|
|
303
|
-
|
|
304
|
-
// This can be improved, for now we just need something to test with
|
|
305
|
-
const elements =
|
|
306
|
-
iframe.current?.contentWindow?.document.querySelectorAll<HTMLElement>(
|
|
307
|
-
`[data-tinafield]`
|
|
308
|
-
)
|
|
309
|
-
if (elements) {
|
|
310
|
-
for (let i = 0; i < elements.length; i++) {
|
|
311
|
-
const el = elements[i]
|
|
312
|
-
el.onclick = () => {
|
|
313
|
-
const tinafield = el.getAttribute('data-tinafield')
|
|
314
|
-
cms.events.dispatch({
|
|
315
|
-
type: 'field:selected',
|
|
316
|
-
value: tinafield,
|
|
317
|
-
})
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
463
|
}
|
|
464
|
+
cms.dispatch({
|
|
465
|
+
type: 'form-lists:add',
|
|
466
|
+
value: {
|
|
467
|
+
id: payload.id,
|
|
468
|
+
label: 'Anonymous Query', // TODO: grab the name of the query if it exists
|
|
469
|
+
items: formListItems,
|
|
470
|
+
formIds,
|
|
471
|
+
},
|
|
472
|
+
})
|
|
322
473
|
},
|
|
323
474
|
[resolvedDocuments.map((doc) => doc._internalSys.path).join('.')]
|
|
324
475
|
)
|
|
325
476
|
|
|
326
|
-
const
|
|
477
|
+
const handleMessage = React.useCallback(
|
|
327
478
|
(event: MessageEvent<PostMessage>) => {
|
|
479
|
+
if (event?.data?.type === 'quick-edit') {
|
|
480
|
+
cms.dispatch({
|
|
481
|
+
type: 'set-quick-editing-supported',
|
|
482
|
+
value: event.data.value,
|
|
483
|
+
})
|
|
484
|
+
iframe.current?.contentWindow?.postMessage({
|
|
485
|
+
type: 'quickEditEnabled',
|
|
486
|
+
value: cms.state.sidebarDisplayState === 'open',
|
|
487
|
+
})
|
|
488
|
+
}
|
|
328
489
|
if (event?.data?.type === 'isEditMode') {
|
|
329
490
|
iframe?.current?.contentWindow?.postMessage({
|
|
330
491
|
type: 'tina:editMode',
|
|
331
492
|
})
|
|
332
493
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
494
|
+
if (event.data.type === 'field:selected') {
|
|
495
|
+
const [queryId, eventFieldName] = event.data.fieldName.split('---')
|
|
496
|
+
const result = results.find((res) => res.id === queryId)
|
|
497
|
+
if (result?.data) {
|
|
498
|
+
cms.dispatch({
|
|
499
|
+
type: 'forms:set-active-field-name',
|
|
500
|
+
value: getFormAndFieldNameFromMetadata(result.data, eventFieldName),
|
|
501
|
+
})
|
|
502
|
+
}
|
|
503
|
+
cms.dispatch({
|
|
504
|
+
type: 'sidebar:set-display-state',
|
|
505
|
+
value: 'openOrFull',
|
|
506
|
+
})
|
|
507
|
+
}
|
|
338
508
|
if (event.data.type === 'close') {
|
|
339
509
|
const payloadSchema = z.object({ id: z.string() })
|
|
340
510
|
const { id } = payloadSchema.parse(event.data)
|
|
341
511
|
setPayloads((previous) =>
|
|
342
512
|
previous.filter((payload) => payload.id !== id)
|
|
343
513
|
)
|
|
514
|
+
setResults((previous) => previous.filter((result) => result.id !== id))
|
|
344
515
|
cms.forms.all().map((form) => {
|
|
345
516
|
form.removeQuery(id)
|
|
346
517
|
})
|
|
347
518
|
cms.removeOrphanedForms()
|
|
519
|
+
cms.dispatch({ type: 'form-lists:remove', value: id })
|
|
348
520
|
}
|
|
349
521
|
if (event.data.type === 'open') {
|
|
350
522
|
const payloadSchema = z.object({
|
|
@@ -360,7 +532,7 @@ export const useGraphQLReducer = (
|
|
|
360
532
|
])
|
|
361
533
|
}
|
|
362
534
|
},
|
|
363
|
-
[cms]
|
|
535
|
+
[cms, JSON.stringify(results)]
|
|
364
536
|
)
|
|
365
537
|
|
|
366
538
|
React.useEffect(() => {
|
|
@@ -374,22 +546,31 @@ export const useGraphQLReducer = (
|
|
|
374
546
|
React.useEffect(() => {
|
|
375
547
|
return () => {
|
|
376
548
|
setPayloads([])
|
|
549
|
+
setResults([])
|
|
377
550
|
cms.removeAllForms()
|
|
551
|
+
cms.dispatch({ type: 'form-lists:clear' })
|
|
378
552
|
}
|
|
379
553
|
}, [url])
|
|
380
554
|
|
|
381
555
|
React.useEffect(() => {
|
|
556
|
+
iframe.current?.contentWindow?.postMessage({
|
|
557
|
+
type: 'quickEditEnabled',
|
|
558
|
+
value: cms.state.sidebarDisplayState === 'open',
|
|
559
|
+
})
|
|
560
|
+
}, [cms.state.sidebarDisplayState])
|
|
561
|
+
|
|
562
|
+
React.useEffect(() => {
|
|
563
|
+
cms.dispatch({ type: 'set-edit-mode', value: 'visual' })
|
|
382
564
|
if (iframe) {
|
|
383
|
-
window.addEventListener('message',
|
|
384
|
-
window.addEventListener('message', notifyEditMode)
|
|
565
|
+
window.addEventListener('message', handleMessage)
|
|
385
566
|
}
|
|
386
567
|
|
|
387
568
|
return () => {
|
|
388
|
-
window.removeEventListener('message',
|
|
389
|
-
window.removeEventListener('message', notifyEditMode)
|
|
569
|
+
window.removeEventListener('message', handleMessage)
|
|
390
570
|
cms.removeAllForms()
|
|
571
|
+
cms.dispatch({ type: 'set-edit-mode', value: 'basic' })
|
|
391
572
|
}
|
|
392
|
-
}, [iframe.current])
|
|
573
|
+
}, [iframe.current, JSON.stringify(results)])
|
|
393
574
|
}
|
|
394
575
|
|
|
395
576
|
const onSubmit = async (
|
|
@@ -427,7 +608,8 @@ type Path = (string | number)[]
|
|
|
427
608
|
const resolveDocument = (
|
|
428
609
|
doc: ResolvedDocument,
|
|
429
610
|
template: Template<true>,
|
|
430
|
-
form: Form
|
|
611
|
+
form: Form,
|
|
612
|
+
pathToDocument: string
|
|
431
613
|
): ResolvedDocument => {
|
|
432
614
|
// @ts-ignore AnyField and TinaField don't mix
|
|
433
615
|
const fields = form.fields as TinaField<true>[]
|
|
@@ -438,6 +620,7 @@ const resolveDocument = (
|
|
|
438
620
|
values: form.values,
|
|
439
621
|
path,
|
|
440
622
|
id,
|
|
623
|
+
pathToDocument,
|
|
441
624
|
})
|
|
442
625
|
const metadataFields: Record<string, string> = {}
|
|
443
626
|
Object.keys(formValues).forEach((key) => {
|
|
@@ -450,7 +633,9 @@ const resolveDocument = (
|
|
|
450
633
|
sys: doc._internalSys,
|
|
451
634
|
values: form.values,
|
|
452
635
|
_tina_metadata: {
|
|
636
|
+
prefix: pathToDocument,
|
|
453
637
|
id: doc._internalSys.path,
|
|
638
|
+
name: '',
|
|
454
639
|
fields: metadataFields,
|
|
455
640
|
},
|
|
456
641
|
_internalSys: doc._internalSys,
|
|
@@ -464,12 +649,14 @@ const resolveFormValue = <T extends Record<string, unknown>>({
|
|
|
464
649
|
values,
|
|
465
650
|
path,
|
|
466
651
|
id,
|
|
652
|
+
pathToDocument,
|
|
467
653
|
}: // tinaSchema,
|
|
468
654
|
{
|
|
469
655
|
fields: TinaField<true>[]
|
|
470
656
|
values: T
|
|
471
657
|
path: Path
|
|
472
658
|
id: string
|
|
659
|
+
pathToDocument: string
|
|
473
660
|
// tinaSchema: TinaSchema
|
|
474
661
|
}): T & { __typename?: string } => {
|
|
475
662
|
const accum: Record<string, unknown> = {}
|
|
@@ -486,6 +673,7 @@ const resolveFormValue = <T extends Record<string, unknown>>({
|
|
|
486
673
|
value: v,
|
|
487
674
|
path,
|
|
488
675
|
id,
|
|
676
|
+
pathToDocument,
|
|
489
677
|
})
|
|
490
678
|
})
|
|
491
679
|
return accum as T & { __typename?: string }
|
|
@@ -495,11 +683,13 @@ const resolveFieldValue = ({
|
|
|
495
683
|
value,
|
|
496
684
|
path,
|
|
497
685
|
id,
|
|
686
|
+
pathToDocument,
|
|
498
687
|
}: {
|
|
499
688
|
field: TinaField<true>
|
|
500
689
|
value: unknown
|
|
501
690
|
path: Path
|
|
502
691
|
id: string
|
|
692
|
+
pathToDocument: string
|
|
503
693
|
}) => {
|
|
504
694
|
switch (field.type) {
|
|
505
695
|
case 'object': {
|
|
@@ -520,13 +710,16 @@ const resolveFieldValue = ({
|
|
|
520
710
|
__typename: NAMER.dataTypeName(template.namespace),
|
|
521
711
|
_tina_metadata: {
|
|
522
712
|
id,
|
|
713
|
+
name: nextPath.join('.'),
|
|
523
714
|
fields: metadataFields,
|
|
715
|
+
prefix: pathToDocument,
|
|
524
716
|
},
|
|
525
717
|
...resolveFormValue({
|
|
526
718
|
fields: template.fields,
|
|
527
719
|
values: item,
|
|
528
720
|
path: nextPath,
|
|
529
721
|
id,
|
|
722
|
+
pathToDocument,
|
|
530
723
|
}),
|
|
531
724
|
}
|
|
532
725
|
})
|
|
@@ -555,13 +748,16 @@ const resolveFieldValue = ({
|
|
|
555
748
|
__typename: NAMER.dataTypeName(field.namespace),
|
|
556
749
|
_tina_metadata: {
|
|
557
750
|
id,
|
|
751
|
+
name: nextPath.join('.'),
|
|
558
752
|
fields: metadataFields,
|
|
753
|
+
prefix: pathToDocument,
|
|
559
754
|
},
|
|
560
755
|
...resolveFormValue({
|
|
561
756
|
fields: templateFields,
|
|
562
757
|
values: item,
|
|
563
|
-
path,
|
|
758
|
+
path: nextPath,
|
|
564
759
|
id,
|
|
760
|
+
pathToDocument,
|
|
565
761
|
}),
|
|
566
762
|
}
|
|
567
763
|
})
|
|
@@ -576,13 +772,16 @@ const resolveFieldValue = ({
|
|
|
576
772
|
__typename: NAMER.dataTypeName(field.namespace),
|
|
577
773
|
_tina_metadata: {
|
|
578
774
|
id,
|
|
775
|
+
name: nextPath.join('.'),
|
|
579
776
|
fields: metadataFields,
|
|
777
|
+
prefix: pathToDocument,
|
|
580
778
|
},
|
|
581
779
|
...resolveFormValue({
|
|
582
780
|
fields: templateFields,
|
|
583
781
|
values: value as any,
|
|
584
|
-
path,
|
|
782
|
+
path: nextPath,
|
|
585
783
|
id,
|
|
784
|
+
pathToDocument,
|
|
586
785
|
}),
|
|
587
786
|
}
|
|
588
787
|
}
|
|
@@ -645,7 +844,7 @@ const expandPayload = async (payload: Payload, cms: TinaCMS) => {
|
|
|
645
844
|
documentNode,
|
|
646
845
|
})
|
|
647
846
|
const expandedQueryForResolver = G.print(expandedDocumentNodeForResolver)
|
|
648
|
-
return { ...payload,
|
|
847
|
+
return { ...payload, expandedQuery, expandedData, expandedQueryForResolver }
|
|
649
848
|
}
|
|
650
849
|
|
|
651
850
|
/**
|
|
@@ -738,12 +937,10 @@ const buildForm = ({
|
|
|
738
937
|
}
|
|
739
938
|
if (form) {
|
|
740
939
|
if (shouldRegisterForm) {
|
|
741
|
-
form.subscribe(() => {}, { values: true })
|
|
742
940
|
if (collection.ui?.global) {
|
|
743
941
|
cms.plugins.add(new GlobalFormPlugin(form))
|
|
744
|
-
} else {
|
|
745
|
-
cms.forms.add(form)
|
|
746
942
|
}
|
|
943
|
+
cms.dispatch({ type: 'forms:add', value: form })
|
|
747
944
|
}
|
|
748
945
|
}
|
|
749
946
|
if (!form) {
|
package/src/lib/types.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
export type PostMessage =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export type PostMessage =
|
|
2
|
+
| {
|
|
3
|
+
type: 'open' | 'close' | 'isEditMode'
|
|
4
|
+
id: string
|
|
5
|
+
data: object
|
|
6
|
+
}
|
|
7
|
+
| { type: 'field:selected'; fieldName: string }
|
|
8
|
+
| { type: 'quick-edit'; value: boolean }
|
|
6
9
|
|
|
7
10
|
export type Payload = {
|
|
8
11
|
id: string
|
package/src/lib/util.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const charCodeOfDot = '.'.charCodeAt(0)
|
|
2
|
+
const reEscapeChar = /\\(\\)?/g
|
|
3
|
+
const rePropName = RegExp(
|
|
4
|
+
// Match anything that isn't a dot or bracket.
|
|
5
|
+
'[^.[\\]]+' +
|
|
6
|
+
'|' +
|
|
7
|
+
// Or match property names within brackets.
|
|
8
|
+
'\\[(?:' +
|
|
9
|
+
// Match a non-string expression.
|
|
10
|
+
'([^"\'][^[]*)' +
|
|
11
|
+
'|' +
|
|
12
|
+
// Or match strings (supports escaping characters).
|
|
13
|
+
'(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
|
|
14
|
+
')\\]' +
|
|
15
|
+
'|' +
|
|
16
|
+
// Or match "" as the space between consecutive dots or empty brackets.
|
|
17
|
+
'(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))',
|
|
18
|
+
'g'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Converts `string` to a property path array.
|
|
23
|
+
*
|
|
24
|
+
* @private
|
|
25
|
+
* @param {string} string The string to convert.
|
|
26
|
+
* @returns {Array} Returns the property path array.
|
|
27
|
+
*/
|
|
28
|
+
const stringToPath = (string: string) => {
|
|
29
|
+
const result = []
|
|
30
|
+
if (string.charCodeAt(0) === charCodeOfDot) {
|
|
31
|
+
result.push('')
|
|
32
|
+
}
|
|
33
|
+
string.replace(rePropName, (match, expression, quote, subString) => {
|
|
34
|
+
let key = match
|
|
35
|
+
if (quote) {
|
|
36
|
+
key = subString.replace(reEscapeChar, '$1')
|
|
37
|
+
} else if (expression) {
|
|
38
|
+
key = expression.trim()
|
|
39
|
+
}
|
|
40
|
+
result.push(key)
|
|
41
|
+
})
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const keysCache: { [key: string]: string[] } = {}
|
|
46
|
+
const keysRegex = /[.[\]]+/
|
|
47
|
+
|
|
48
|
+
const toPath = (key: string): string[] => {
|
|
49
|
+
if (key === null || key === undefined || !key.length) {
|
|
50
|
+
return []
|
|
51
|
+
}
|
|
52
|
+
if (typeof key !== 'string') {
|
|
53
|
+
throw new Error('toPath() expects a string')
|
|
54
|
+
}
|
|
55
|
+
if (keysCache[key] == null) {
|
|
56
|
+
/**
|
|
57
|
+
* The following patch fixes issue 456, introduced since v4.20.3:
|
|
58
|
+
*
|
|
59
|
+
* Before v4.20.3, i.e. in v4.20.2, a `key` like 'choices[]' would map to ['choices']
|
|
60
|
+
* (e.g. an array of choices used where 'choices[]' is name attribute of an input of type checkbox).
|
|
61
|
+
*
|
|
62
|
+
* Since v4.20.3, a `key` like 'choices[]' would map to ['choices', ''] which is wrong and breaks
|
|
63
|
+
* this kind of inputs e.g. in React.
|
|
64
|
+
*
|
|
65
|
+
* v4.20.3 introduced an unwanted breaking change, this patch fixes it, see the issue at the link below.
|
|
66
|
+
*
|
|
67
|
+
* @see https://github.com/final-form/final-form/issues/456
|
|
68
|
+
*/
|
|
69
|
+
if (key.endsWith('[]')) {
|
|
70
|
+
// v4.20.2 (a `key` like 'choices[]' should map to ['choices'], which is fine).
|
|
71
|
+
keysCache[key] = key.split(keysRegex).filter(Boolean)
|
|
72
|
+
} else {
|
|
73
|
+
// v4.20.3 (a `key` like 'choices[]' maps to ['choices', ''], which breaks applications relying on inputs like `<input type="checkbox" name="choices[]" />`).
|
|
74
|
+
keysCache[key] = stringToPath(key)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return keysCache[key]
|
|
78
|
+
}
|
|
79
|
+
export const getDeepestMetadata = (state: Object, complexKey: string): any => {
|
|
80
|
+
// Intentionally using iteration rather than recursion
|
|
81
|
+
const path = toPath(complexKey)
|
|
82
|
+
let current: any = state
|
|
83
|
+
let metadata: any
|
|
84
|
+
for (let i = 0; i < path.length; i++) {
|
|
85
|
+
const key = path[i]
|
|
86
|
+
if (
|
|
87
|
+
current === undefined ||
|
|
88
|
+
current === null ||
|
|
89
|
+
typeof current !== 'object' ||
|
|
90
|
+
(Array.isArray(current) && isNaN(Number(key)))
|
|
91
|
+
) {
|
|
92
|
+
return undefined
|
|
93
|
+
}
|
|
94
|
+
const value = current[key]
|
|
95
|
+
if (value?._tina_metadata) {
|
|
96
|
+
metadata = value._tina_metadata
|
|
97
|
+
}
|
|
98
|
+
current = value
|
|
99
|
+
}
|
|
100
|
+
return metadata
|
|
101
|
+
}
|
|
102
|
+
export const getFormAndFieldNameFromMetadata = (
|
|
103
|
+
object: object,
|
|
104
|
+
eventFieldName: string
|
|
105
|
+
) => {
|
|
106
|
+
let formId
|
|
107
|
+
let n
|
|
108
|
+
const value = getDeepestMetadata(object, eventFieldName)
|
|
109
|
+
if (value) {
|
|
110
|
+
if (value.prefix) {
|
|
111
|
+
const fieldName = eventFieldName.slice(value?.prefix?.length + 1)
|
|
112
|
+
const localFieldName = value.name
|
|
113
|
+
? fieldName.slice(value?.name?.length + 1)
|
|
114
|
+
: fieldName
|
|
115
|
+
if (localFieldName) {
|
|
116
|
+
// If localFieldName is tags.2, just use `tags`
|
|
117
|
+
if (!isNaN(Number(localFieldName.split('.')[1]))) {
|
|
118
|
+
n = value.fields[localFieldName.split('.')[0]]
|
|
119
|
+
} else {
|
|
120
|
+
n = value.fields[localFieldName]
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
n = value.name
|
|
124
|
+
}
|
|
125
|
+
formId = value.id
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { formId, fieldName: n }
|
|
129
|
+
}
|