@sanity/document-internationalization 4.1.1 → 5.0.1

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.
Files changed (52) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +44 -36
  3. package/dist/_chunks-es/{resources.mjs → resources.js} +1 -1
  4. package/dist/{_chunks-cjs → _chunks-es}/resources.js.map +1 -1
  5. package/dist/index.d.ts +112 -88
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +1075 -1337
  8. package/dist/index.js.map +1 -1
  9. package/package.json +35 -69
  10. package/dist/_chunks-cjs/resources.js +0 -8
  11. package/dist/_chunks-es/resources.mjs.map +0 -1
  12. package/dist/_legacy/resources.esm.js +0 -9
  13. package/dist/_legacy/resources.esm.js.map +0 -1
  14. package/dist/index.d.mts +0 -108
  15. package/dist/index.esm.js +0 -1625
  16. package/dist/index.esm.js.map +0 -1
  17. package/dist/index.mjs +0 -1625
  18. package/dist/index.mjs.map +0 -1
  19. package/sanity.json +0 -8
  20. package/src/actions/DeleteMetadataAction.tsx +0 -93
  21. package/src/actions/DeleteTranslationAction.tsx +0 -102
  22. package/src/actions/DuplicateWithTranslationsAction.tsx +0 -251
  23. package/src/badges/index.tsx +0 -27
  24. package/src/components/BulkPublish/DocumentCheck.tsx +0 -90
  25. package/src/components/BulkPublish/Info.tsx +0 -28
  26. package/src/components/BulkPublish/InfoIcon.tsx +0 -34
  27. package/src/components/BulkPublish/index.tsx +0 -181
  28. package/src/components/ConstrainedBox.tsx +0 -6
  29. package/src/components/DeleteTranslationDialog/DocumentPreview.tsx +0 -19
  30. package/src/components/DeleteTranslationDialog/index.tsx +0 -111
  31. package/src/components/DeleteTranslationDialog/separateReferences.ts +0 -23
  32. package/src/components/DeleteTranslationFooter.tsx +0 -28
  33. package/src/components/DocumentInternationalizationContext.tsx +0 -47
  34. package/src/components/DocumentInternationalizationMenu.tsx +0 -235
  35. package/src/components/LanguageManage.tsx +0 -108
  36. package/src/components/LanguageOption.tsx +0 -259
  37. package/src/components/LanguagePatch.tsx +0 -67
  38. package/src/components/OptimisticallyStrengthen/ReferencePatcher.tsx +0 -50
  39. package/src/components/OptimisticallyStrengthen/index.tsx +0 -34
  40. package/src/components/Warning.tsx +0 -18
  41. package/src/constants.ts +0 -16
  42. package/src/hooks/useLanguageMetadata.tsx +0 -26
  43. package/src/hooks/useOpenInNewPane.tsx +0 -33
  44. package/src/i18n/index.ts +0 -21
  45. package/src/i18n/resources.ts +0 -7
  46. package/src/index.ts +0 -6
  47. package/src/plugin.tsx +0 -239
  48. package/src/schema/translation/metadata.ts +0 -68
  49. package/src/types.ts +0 -97
  50. package/src/utils/createReference.ts +0 -20
  51. package/src/utils/excludePaths.ts +0 -123
  52. package/v2-incompatible.js +0 -11
@@ -1,251 +0,0 @@
1
- import {CopyIcon} from '@sanity/icons'
2
- import {useToast} from '@sanity/ui'
3
- import {uuid} from '@sanity/uuid'
4
- import {useCallback, useMemo, useState} from 'react'
5
- import {filter, firstValueFrom} from 'rxjs'
6
- import {
7
- DEFAULT_STUDIO_CLIENT_OPTIONS,
8
- type DocumentActionComponent,
9
- type Id,
10
- InsufficientPermissionsMessage,
11
- type PatchOperations,
12
- useClient,
13
- useCurrentUser,
14
- useDocumentOperation,
15
- useDocumentPairPermissions,
16
- useDocumentStore,
17
- useTranslation,
18
- } from 'sanity'
19
- import {useRouter} from 'sanity/router'
20
- import {structureLocaleNamespace} from 'sanity/structure'
21
-
22
- import {METADATA_SCHEMA_NAME, TRANSLATIONS_ARRAY_NAME} from '../constants'
23
- import {useTranslationMetadata} from '../hooks/useLanguageMetadata'
24
- import {documenti18nLocaleNamespace} from '../i18n'
25
-
26
- const DISABLED_REASON_KEY = {
27
- METADATA_NOT_FOUND: 'action.duplicate.disabled.missing-metadata',
28
- MULTIPLE_METADATA: 'action.duplicate.disabled.multiple-metadata',
29
- NOTHING_TO_DUPLICATE: 'action.duplicate.disabled.nothing-to-duplicate',
30
- NOT_READY: 'action.duplicate.disabled.not-ready',
31
- }
32
-
33
- export const DuplicateWithTranslationsAction: DocumentActionComponent = ({
34
- id,
35
- type,
36
- onComplete,
37
- }) => {
38
- const documentStore = useDocumentStore()
39
- const {duplicate} = useDocumentOperation(id, type)
40
- const {navigateIntent} = useRouter()
41
- const [isDuplicating, setDuplicating] = useState(false)
42
- const [permissions, isPermissionsLoading] = useDocumentPairPermissions({
43
- id,
44
- type,
45
- permission: 'duplicate',
46
- })
47
- const {data, loading: isMetadataDocumentLoading} = useTranslationMetadata(id)
48
- const hasOneMetadataDocument = useMemo(() => {
49
- return Array.isArray(data) && data.length <= 1
50
- }, [data])
51
- const metadataDocument = Array.isArray(data) && data.length ? data[0] : null
52
- const client = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS)
53
- const toast = useToast()
54
- const {t: s} = useTranslation(structureLocaleNamespace)
55
- const {t: d} = useTranslation(documenti18nLocaleNamespace)
56
- const currentUser = useCurrentUser()
57
-
58
- const handle = useCallback(async () => {
59
- setDuplicating(true)
60
-
61
- try {
62
- if (!metadataDocument) {
63
- throw new Error('Metadata document not found')
64
- }
65
-
66
- // 1. Duplicate the document and its localized versions
67
- const translations = new Map<string, Id>()
68
- await Promise.all(
69
- metadataDocument[TRANSLATIONS_ARRAY_NAME].map(async (translation) => {
70
- const dupeId = uuid()
71
- const locale = translation._key
72
- const docId = translation.value?._ref
73
-
74
- if (!docId) {
75
- throw new Error('Translation document not found')
76
- }
77
-
78
- const {duplicate: duplicateTranslation} = await firstValueFrom(
79
- documentStore.pair
80
- .editOperations(docId, type)
81
- .pipe(filter((op) => op.duplicate.disabled !== 'NOT_READY'))
82
- )
83
-
84
- if (duplicateTranslation.disabled) {
85
- throw new Error('Cannot duplicate document')
86
- }
87
-
88
- const duplicateTranslationSuccess = firstValueFrom(
89
- documentStore.pair
90
- .operationEvents(docId, type)
91
- .pipe(filter((e) => e.op === 'duplicate' && e.type === 'success'))
92
- )
93
- duplicateTranslation.execute(dupeId)
94
- await duplicateTranslationSuccess
95
-
96
- translations.set(locale, dupeId)
97
- })
98
- )
99
-
100
- // 2. Duplicate the metadata document
101
- const {duplicate: duplicateMetadata} = await firstValueFrom(
102
- documentStore.pair
103
- .editOperations(metadataDocument._id, METADATA_SCHEMA_NAME)
104
- .pipe(filter((op) => op.duplicate.disabled !== 'NOT_READY'))
105
- )
106
-
107
- if (duplicateMetadata.disabled) {
108
- throw new Error('Cannot duplicate document')
109
- }
110
-
111
- const duplicateMetadataSuccess = firstValueFrom(
112
- documentStore.pair
113
- .operationEvents(metadataDocument._id, METADATA_SCHEMA_NAME)
114
- .pipe(filter((e) => e.op === 'duplicate' && e.type === 'success'))
115
- )
116
- const dupeId = uuid()
117
- duplicateMetadata.execute(dupeId)
118
- await duplicateMetadataSuccess
119
-
120
- // 3. Patch the duplicated metadata document to update the references
121
- // TODO: use document store
122
- // const {patch: patchMetadata} = await firstValueFrom(
123
- // documentStore.pair
124
- // .editOperations(dupeId, METADATA_SCHEMA_NAME)
125
- // .pipe(filter((op) => op.patch.disabled !== 'NOT_READY'))
126
- // )
127
-
128
- // if (patchMetadata.disabled) {
129
- // throw new Error('Cannot patch document')
130
- // }
131
-
132
- // await firstValueFrom(
133
- // documentStore.pair
134
- // .consistencyStatus(dupeId, METADATA_SCHEMA_NAME)
135
- // .pipe(filter((isConsistant) => isConsistant))
136
- // )
137
-
138
- // const patchMetadataSuccess = firstValueFrom(
139
- // documentStore.pair
140
- // .operationEvents(dupeId, METADATA_SCHEMA_NAME)
141
- // .pipe(filter((e) => e.op === 'patch' && e.type === 'success'))
142
- // )
143
-
144
- const patch: PatchOperations = {
145
- set: Object.fromEntries(
146
- Array.from(translations.entries()).map(([locale, documentId]) => [
147
- `${TRANSLATIONS_ARRAY_NAME}[_key == "${locale}"].value._ref`,
148
- documentId,
149
- ])
150
- ),
151
- }
152
-
153
- // patchMetadata.execute([patch])
154
- // await patchMetadataSuccess
155
- await client.transaction().patch(dupeId, patch).commit()
156
-
157
- // 4. Navigate to the duplicated document
158
- navigateIntent('edit', {
159
- id: Array.from(translations.values()).at(0),
160
- type,
161
- })
162
-
163
- onComplete()
164
- } catch (error) {
165
- console.error(error)
166
- toast.push({
167
- status: 'error',
168
- title: 'Error duplicating document',
169
- description:
170
- error instanceof Error
171
- ? error.message
172
- : 'Failed to duplicate document',
173
- })
174
- } finally {
175
- setDuplicating(false)
176
- }
177
- }, [
178
- client,
179
- documentStore.pair,
180
- metadataDocument,
181
- navigateIntent,
182
- onComplete,
183
- toast,
184
- type,
185
- ])
186
-
187
- return useMemo(() => {
188
- if (!isPermissionsLoading && !permissions?.granted) {
189
- return {
190
- icon: CopyIcon,
191
- disabled: true,
192
- label: d('action.duplicate.label'),
193
- title: (
194
- <InsufficientPermissionsMessage
195
- context="duplicate-document"
196
- currentUser={currentUser}
197
- />
198
- ),
199
- }
200
- }
201
-
202
- if (!isMetadataDocumentLoading && !metadataDocument) {
203
- return {
204
- icon: CopyIcon,
205
- disabled: true,
206
- label: d('action.duplicate.label'),
207
- title: d(DISABLED_REASON_KEY.METADATA_NOT_FOUND),
208
- }
209
- }
210
-
211
- if (!hasOneMetadataDocument) {
212
- return {
213
- icon: CopyIcon,
214
- disabled: true,
215
- label: d('action.duplicate.label'),
216
- title: d(DISABLED_REASON_KEY.MULTIPLE_METADATA),
217
- }
218
- }
219
-
220
- return {
221
- icon: CopyIcon,
222
- disabled:
223
- isDuplicating ||
224
- Boolean(duplicate.disabled) ||
225
- isPermissionsLoading ||
226
- isMetadataDocumentLoading,
227
- label: isDuplicating
228
- ? s('action.duplicate.running.label')
229
- : d('action.duplicate.label'),
230
- title: duplicate.disabled
231
- ? s(DISABLED_REASON_KEY[duplicate.disabled])
232
- : '',
233
- onHandle: handle,
234
- }
235
- }, [
236
- currentUser,
237
- duplicate.disabled,
238
- handle,
239
- hasOneMetadataDocument,
240
- isDuplicating,
241
- isMetadataDocumentLoading,
242
- isPermissionsLoading,
243
- metadataDocument,
244
- permissions?.granted,
245
- s,
246
- d,
247
- ])
248
- }
249
-
250
- DuplicateWithTranslationsAction.action = 'duplicate'
251
- DuplicateWithTranslationsAction.displayName = 'DuplicateWithTranslationsAction'
@@ -1,27 +0,0 @@
1
- import type {DocumentBadgeDescription, DocumentBadgeProps} from 'sanity'
2
-
3
- import {useDocumentInternationalizationContext} from '../components/DocumentInternationalizationContext'
4
-
5
- export function LanguageBadge(
6
- props: DocumentBadgeProps
7
- ): DocumentBadgeDescription | null {
8
- const source = props?.draft || props?.published
9
- const {languageField, supportedLanguages} =
10
- useDocumentInternationalizationContext()
11
- const languageId = source?.[languageField]
12
-
13
- if (!languageId) {
14
- return null
15
- }
16
-
17
- const language = Array.isArray(supportedLanguages)
18
- ? supportedLanguages.find((l) => l.id === languageId)
19
- : null
20
-
21
- // Currently we only show the language id if the supportedLanguages are async
22
- return {
23
- label: language?.id ?? String(languageId),
24
- title: language?.title ?? undefined,
25
- color: `primary`,
26
- }
27
- }
@@ -1,90 +0,0 @@
1
- import {Card, Spinner} from '@sanity/ui'
2
- import {useEffect, useMemo} from 'react'
3
- import {Preview, useEditState, useSchema, useValidationStatus} from 'sanity'
4
-
5
- type DocumentCheckProps = {
6
- id: string
7
- onCheckComplete: (id: string) => void
8
- addInvalidId: (id: string) => void
9
- removeInvalidId: (id: string) => void
10
- addDraftId: (id: string) => void
11
- removeDraftId: (id: string) => void
12
- }
13
-
14
- // Check if the document has a draft
15
- // Check if that draft is valid
16
- // Report back to parent that it can be added to bulk publish
17
- export default function DocumentCheck(props: DocumentCheckProps) {
18
- const {
19
- id,
20
- onCheckComplete,
21
- addInvalidId,
22
- removeInvalidId,
23
- addDraftId,
24
- removeDraftId,
25
- } = props
26
- const editState = useEditState(id, ``)
27
- const {isValidating, validation} = useValidationStatus(id, ``)
28
- const schema = useSchema()
29
-
30
- const validationHasErrors = useMemo(() => {
31
- return (
32
- !isValidating &&
33
- validation.length > 0 &&
34
- validation.some((item) => item.level === 'error')
35
- )
36
- }, [isValidating, validation])
37
-
38
- useEffect(() => {
39
- if (validationHasErrors) {
40
- addInvalidId(id)
41
- } else {
42
- removeInvalidId(id)
43
- }
44
-
45
- if (editState.draft) {
46
- addDraftId(id)
47
- } else {
48
- removeDraftId(id)
49
- }
50
-
51
- if (!isValidating) {
52
- onCheckComplete(id)
53
- }
54
- }, [
55
- addDraftId,
56
- addInvalidId,
57
- editState.draft,
58
- id,
59
- isValidating,
60
- onCheckComplete,
61
- removeDraftId,
62
- removeInvalidId,
63
- validationHasErrors,
64
- ])
65
-
66
- // We only care about drafts
67
- if (!editState.draft) {
68
- return null
69
- }
70
-
71
- const schemaType = schema.get(editState.draft._type)
72
-
73
- return (
74
- <Card
75
- border
76
- padding={2}
77
- tone={validationHasErrors ? `critical` : `positive`}
78
- >
79
- {editState.draft && schemaType ? (
80
- <Preview
81
- layout="default"
82
- value={editState.draft}
83
- schemaType={schemaType}
84
- />
85
- ) : (
86
- <Spinner />
87
- )}
88
- </Card>
89
- )
90
- }
@@ -1,28 +0,0 @@
1
- import {InfoOutlineIcon} from '@sanity/icons'
2
- import {Box, Stack, Text} from '@sanity/ui'
3
-
4
- import InfoIcon from './InfoIcon'
5
-
6
- export default function Info() {
7
- return (
8
- <InfoIcon icon={InfoOutlineIcon} tone="primary">
9
- <Stack padding={3} space={4} style={{maxWidth: 250}}>
10
- <Box>
11
- <Text size={1}>Bulk publishing uses the Scheduling API.</Text>
12
- </Box>
13
- <Box>
14
- <Text size={1}>
15
- Customized Document Actions in the Studio will not execute. Webhooks
16
- will execute.
17
- </Text>
18
- </Box>
19
- <Box>
20
- <Text size={1}>
21
- Validation is checked before rendering the button below, but the
22
- Scheduling API will not check for – or enforce – validation.
23
- </Text>
24
- </Box>
25
- </Stack>
26
- </InfoIcon>
27
- )
28
- }
@@ -1,34 +0,0 @@
1
- import {Box, type ButtonTone, Text, Tooltip} from '@sanity/ui'
2
- import type {ComponentType, PropsWithChildren} from 'react'
3
- import {TextWithTone} from 'sanity'
4
-
5
- type InfoIconProps = PropsWithChildren & {
6
- icon: ComponentType
7
- tone: ButtonTone
8
- text?: string
9
- }
10
-
11
- export default function InfoIcon(props: InfoIconProps) {
12
- const {text, icon, tone, children} = props
13
- const Icon = icon
14
-
15
- return (
16
- <Tooltip
17
- animate
18
- portal
19
- content={
20
- children ? (
21
- <>{children}</>
22
- ) : (
23
- <Box padding={2}>
24
- <Text size={1}>{text}</Text>
25
- </Box>
26
- )
27
- }
28
- >
29
- <TextWithTone tone={tone} size={1}>
30
- <Icon />
31
- </TextWithTone>
32
- </Tooltip>
33
- )
34
- }
@@ -1,181 +0,0 @@
1
- import {Button, Card, Dialog, Inline, Stack, Text, useToast} from '@sanity/ui'
2
- import {useCallback, useState} from 'react'
3
- import {TextWithTone, useClient, useWorkspace} from 'sanity'
4
-
5
- import {API_VERSION} from '../../constants'
6
- import type {TranslationReference} from '../../types'
7
- import DocumentCheck from './DocumentCheck'
8
- import Info from './Info'
9
-
10
- export type BulkPublishProps = {
11
- translations: TranslationReference[]
12
- }
13
-
14
- // A root-level component with UI for hitting the Publishing API
15
- export default function BulkPublish(props: BulkPublishProps) {
16
- const {translations} = props
17
- const client = useClient({apiVersion: API_VERSION})
18
- const {projectId, dataset} = useWorkspace()
19
- const toast = useToast()
20
- const [invalidIds, setInvalidIds] = useState<string[] | null>(null)
21
- const [checkedIds, setCheckedIds] = useState<string[]>([])
22
-
23
- const onCheckComplete = useCallback((id: string) => {
24
- setCheckedIds((ids) => Array.from(new Set([...ids, id])))
25
- }, [])
26
-
27
- // Handle dialog
28
- const [open, setOpen] = useState(false)
29
- const onOpen = useCallback(() => setOpen(true), [])
30
- const onClose = useCallback(() => setOpen(false), [])
31
-
32
- const addInvalidId = useCallback((id: string) => {
33
- setInvalidIds((ids) => (ids ? Array.from(new Set([...ids, id])) : [id]))
34
- }, [])
35
-
36
- const removeInvalidId = useCallback((id: string) => {
37
- setInvalidIds((ids) => (ids ? ids.filter((i) => i !== id) : []))
38
- }, [])
39
-
40
- const [draftIds, setDraftIds] = useState<string[]>([])
41
-
42
- const addDraftId = useCallback((id: string) => {
43
- setDraftIds((ids) => Array.from(new Set([...ids, id])))
44
- }, [])
45
-
46
- const removeDraftId = useCallback((id: string) => {
47
- setDraftIds((ids) => ids.filter((i) => i !== id))
48
- }, [])
49
-
50
- const handleBulkPublish = useCallback(() => {
51
- const body = translations.map((translation) => ({
52
- documentId: translation.value._ref,
53
- }))
54
- client
55
- .request({
56
- uri: `/publish/${projectId}/${dataset}`,
57
- method: 'POST',
58
- body,
59
- })
60
- .then(() => {
61
- toast.push({
62
- status: 'success',
63
- title: 'Success',
64
- description: 'Bulk publish complete',
65
- })
66
- })
67
- .catch((err) => {
68
- console.error(err)
69
- toast.push({
70
- status: 'error',
71
- title: 'Error',
72
- description: 'Bulk publish failed',
73
- })
74
- })
75
- }, [translations, client, projectId, dataset, toast])
76
-
77
- const disabled =
78
- // Not all documents have been checked
79
- checkedIds.length !== translations.length ||
80
- // Some document(s) are invalid
81
- Boolean(invalidIds && invalidIds?.length > 0) ||
82
- // No documents are drafts
83
- !draftIds.length
84
-
85
- return translations?.length > 0 ? (
86
- <Card padding={4} border radius={2}>
87
- <Stack space={3}>
88
- <Inline space={3}>
89
- <Text weight="bold" size={1}>
90
- Bulk publishing
91
- </Text>
92
- <Info />
93
- </Inline>
94
-
95
- <Stack>
96
- <Button
97
- onClick={onOpen}
98
- text="Prepare bulk publishing"
99
- mode="ghost"
100
- />
101
- </Stack>
102
-
103
- {open && (
104
- <Dialog
105
- animate
106
- header="Bulk publishing"
107
- id="bulk-publish-dialog"
108
- onClose={onClose}
109
- zOffset={1000}
110
- width={3}
111
- >
112
- <Stack space={4} padding={4}>
113
- {draftIds.length > 0 ? (
114
- <Stack space={2}>
115
- <Text size={1}>
116
- There{' '}
117
- {draftIds.length === 1
118
- ? `is 1 draft document`
119
- : `are ${draftIds.length} draft documents`}
120
- .
121
- </Text>
122
- {invalidIds && invalidIds.length > 0 ? (
123
- <TextWithTone tone="critical" size={1}>
124
- {invalidIds && invalidIds.length === 1
125
- ? `1 draft document has`
126
- : `${
127
- invalidIds && invalidIds.length
128
- } draft documents have`}{' '}
129
- validation issues that must addressed first
130
- </TextWithTone>
131
- ) : (
132
- <TextWithTone tone="positive" size={1}>
133
- All drafts are valid and can be bulk published
134
- </TextWithTone>
135
- )}
136
- </Stack>
137
- ) : null}
138
-
139
- <Stack space={1}>
140
- {translations
141
- .filter((translation) => translation?.value?._ref)
142
- .map((translation) => (
143
- <DocumentCheck
144
- key={translation._key}
145
- id={translation.value._ref}
146
- onCheckComplete={onCheckComplete}
147
- addInvalidId={addInvalidId}
148
- removeInvalidId={removeInvalidId}
149
- addDraftId={addDraftId}
150
- removeDraftId={removeDraftId}
151
- />
152
- ))}
153
- </Stack>
154
- {draftIds.length > 0 ? (
155
- <Button
156
- mode="ghost"
157
- tone={
158
- invalidIds && invalidIds?.length > 0
159
- ? 'caution'
160
- : 'positive'
161
- }
162
- text={
163
- draftIds.length === 1
164
- ? `Publish draft document`
165
- : `Bulk publish ${draftIds.length} draft documents`
166
- }
167
- onClick={handleBulkPublish}
168
- disabled={disabled}
169
- />
170
- ) : (
171
- <Text muted size={1}>
172
- No draft documents to publish
173
- </Text>
174
- )}
175
- </Stack>
176
- </Dialog>
177
- )}
178
- </Stack>
179
- </Card>
180
- ) : null
181
- }
@@ -1,6 +0,0 @@
1
- import {Box} from '@sanity/ui'
2
- import {styled} from 'styled-components'
3
-
4
- export default styled(Box)`
5
- max-width: 280px;
6
- `
@@ -1,19 +0,0 @@
1
- import {Preview, useSchema} from 'sanity'
2
- import {Feedback} from 'sanity-plugin-utils'
3
-
4
- type DocumentPreviewProps = {
5
- value: unknown
6
- type: string
7
- }
8
-
9
- // Wrapper of Preview just so that the schema type is satisfied by schema.get()
10
- export default function DocumentPreview(props: DocumentPreviewProps) {
11
- const schema = useSchema()
12
-
13
- const schemaType = schema.get(props.type)
14
- if (!schemaType) {
15
- return <Feedback tone="critical" title="Schema type not found" />
16
- }
17
-
18
- return <Preview value={props.value} schemaType={schemaType} />
19
- }