@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,111 +0,0 @@
1
- import {Card, Flex, Spinner, Stack, Text} from '@sanity/ui'
2
- import {useEffect, useMemo} from 'react'
3
- import type {SanityDocument} from 'sanity'
4
- import {useListeningQuery} from 'sanity-plugin-utils'
5
-
6
- import DocumentPreview from './DocumentPreview'
7
- import {separateReferences} from './separateReferences'
8
-
9
- type DeleteTranslationDialogProps = {
10
- doc: SanityDocument
11
- documentId: string
12
- setTranslations: (translations: SanityDocument[]) => void
13
- }
14
-
15
- export default function DeleteTranslationDialog(
16
- props: DeleteTranslationDialogProps
17
- ) {
18
- const {doc, documentId, setTranslations} = props
19
-
20
- // Get all references and check if any of them are translations metadata
21
- const {data, loading} = useListeningQuery<SanityDocument[]>(
22
- `*[references($id)]{_id, _type}`,
23
- {params: {id: documentId}, initialValue: []}
24
- )
25
- const {translations, otherReferences} = useMemo(
26
- () => separateReferences(data as SanityDocument[] | null),
27
- [data]
28
- )
29
-
30
- useEffect(() => {
31
- setTranslations(translations)
32
- }, [setTranslations, translations])
33
-
34
- if (loading) {
35
- return (
36
- <Flex padding={4} align="center" justify="center">
37
- <Spinner />
38
- </Flex>
39
- )
40
- }
41
-
42
- return (
43
- <Stack space={4}>
44
- {translations && translations.length > 0 ? (
45
- <Text>
46
- This document is a language-specific version which other translations
47
- depend on.
48
- </Text>
49
- ) : (
50
- <Text>This document does not have connected translations.</Text>
51
- )}
52
- <Card border padding={3}>
53
- <Stack space={4}>
54
- <Text size={1} weight="semibold">
55
- {translations && translations.length > 0 ? (
56
- <>Before this document can be deleted</>
57
- ) : (
58
- <>This document can now be deleted</>
59
- )}
60
- </Text>
61
- <DocumentPreview value={doc} type={doc._type} />
62
- {translations && translations.length > 0 ? (
63
- <>
64
- <Card borderTop />
65
- <Text size={1} weight="semibold">
66
- The reference in{' '}
67
- {translations.length === 1
68
- ? `this translations document`
69
- : `these translations documents`}{' '}
70
- must be removed
71
- </Text>
72
- {translations.map((translation) => (
73
- <DocumentPreview
74
- key={translation._id}
75
- value={translation}
76
- type={translation._type}
77
- />
78
- ))}
79
- </>
80
- ) : null}
81
- {otherReferences && otherReferences.length > 0 ? (
82
- <>
83
- <Card borderTop />
84
- <Text size={1} weight="semibold">
85
- {otherReferences.length === 1
86
- ? `There is an additional reference`
87
- : `There are additional references`}{' '}
88
- to this document
89
- </Text>
90
- {otherReferences.map((reference) => (
91
- <DocumentPreview
92
- key={reference._id}
93
- value={reference}
94
- type={reference._type}
95
- />
96
- ))}
97
- </>
98
- ) : null}
99
- </Stack>
100
- </Card>
101
- {otherReferences.length === 0 ? (
102
- <Text>This document has no other references.</Text>
103
- ) : (
104
- <Text>
105
- You may not be able to delete this document because other documents
106
- refer to it.
107
- </Text>
108
- )}
109
- </Stack>
110
- )
111
- }
@@ -1,23 +0,0 @@
1
- import type {SanityDocument} from 'sanity'
2
-
3
- import {METADATA_SCHEMA_NAME} from '../../constants'
4
-
5
- export function separateReferences(data: SanityDocument[] | null = []): {
6
- translations: SanityDocument[]
7
- otherReferences: SanityDocument[]
8
- } {
9
- const translations: SanityDocument[] = []
10
- const otherReferences: SanityDocument[] = []
11
-
12
- if (data && data.length > 0) {
13
- data.forEach((doc) => {
14
- if (doc._type === METADATA_SCHEMA_NAME) {
15
- translations.push(doc)
16
- } else {
17
- otherReferences.push(doc)
18
- }
19
- })
20
- }
21
-
22
- return {translations, otherReferences}
23
- }
@@ -1,28 +0,0 @@
1
- import {Button, Grid} from '@sanity/ui'
2
-
3
- type DeleteTranslationFooterProps = {
4
- translations: unknown[]
5
- onClose: () => void
6
- onProceed: () => void
7
- }
8
-
9
- export default function DeleteTranslationFooter(
10
- props: DeleteTranslationFooterProps
11
- ) {
12
- const {translations, onClose, onProceed} = props
13
-
14
- return (
15
- <Grid columns={2} gap={2}>
16
- <Button text="Cancel" onClick={onClose} mode="ghost" />
17
- <Button
18
- text={
19
- translations && translations.length > 0
20
- ? `Unset translation reference`
21
- : `Delete document`
22
- }
23
- onClick={onProceed}
24
- tone="critical"
25
- />
26
- </Grid>
27
- )
28
- }
@@ -1,47 +0,0 @@
1
- import {useContext} from 'react'
2
- import {createContext} from 'react'
3
- import {type LayoutProps, useClient, useWorkspace} from 'sanity'
4
- import {suspend} from 'suspend-react'
5
-
6
- import {DEFAULT_CONFIG} from '../constants'
7
- import type {PluginConfig, PluginConfigContext} from '../types'
8
-
9
- const DocumentInternationalizationContext =
10
- createContext<PluginConfigContext>(DEFAULT_CONFIG)
11
-
12
- export function useDocumentInternationalizationContext() {
13
- return useContext(DocumentInternationalizationContext)
14
- }
15
-
16
- type DocumentInternationalizationProviderProps = LayoutProps & {
17
- pluginConfig: Required<PluginConfig>
18
- }
19
-
20
- /**
21
- * This Provider wraps the Studio and provides the DocumentInternationalization context to document actions and components.
22
- */
23
- export function DocumentInternationalizationProvider(
24
- props: DocumentInternationalizationProviderProps
25
- ) {
26
- const {pluginConfig} = props
27
-
28
- const client = useClient({apiVersion: pluginConfig.apiVersion})
29
- const workspace = useWorkspace()
30
- const supportedLanguages = Array.isArray(pluginConfig.supportedLanguages)
31
- ? pluginConfig.supportedLanguages
32
- : // eslint-disable-next-line require-await
33
- suspend(async () => {
34
- if (typeof pluginConfig.supportedLanguages === 'function') {
35
- return pluginConfig.supportedLanguages(client)
36
- }
37
- return pluginConfig.supportedLanguages
38
- }, [workspace])
39
-
40
- return (
41
- <DocumentInternationalizationContext.Provider
42
- value={{...pluginConfig, supportedLanguages}}
43
- >
44
- {props.renderDefault(props)}
45
- </DocumentInternationalizationContext.Provider>
46
- )
47
- }
@@ -1,235 +0,0 @@
1
- import {TranslateIcon} from '@sanity/icons'
2
- import {
3
- Box,
4
- Button,
5
- Card,
6
- Popover,
7
- Stack,
8
- Text,
9
- TextInput,
10
- useClickOutside,
11
- } from '@sanity/ui'
12
- import {uuid} from '@sanity/uuid'
13
- import {type FormEvent, useCallback, useMemo, useState} from 'react'
14
- import {useEditState} from 'sanity'
15
-
16
- import {useTranslationMetadata} from '../hooks/useLanguageMetadata'
17
- import type {DocumentInternationalizationMenuProps} from '../types'
18
- import {useDocumentInternationalizationContext} from './DocumentInternationalizationContext'
19
- import LanguageManage from './LanguageManage'
20
- import LanguageOption from './LanguageOption'
21
- import LanguagePatch from './LanguagePatch'
22
- import Warning from './Warning'
23
-
24
- export function DocumentInternationalizationMenu(
25
- props: DocumentInternationalizationMenuProps
26
- ) {
27
- const {documentId} = props
28
- const schemaType = props.schemaType
29
- const {languageField, supportedLanguages} =
30
- useDocumentInternationalizationContext()
31
-
32
- // Search filter query
33
- const [query, setQuery] = useState(``)
34
- const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {
35
- if (event.currentTarget.value) {
36
- setQuery(event.currentTarget.value)
37
- } else {
38
- setQuery(``)
39
- }
40
- }, [])
41
-
42
- // UI Handlers
43
- const [open, setOpen] = useState(false)
44
- const handleClick = useCallback(() => setOpen((o) => !o), [])
45
- const [button, setButton] = useState<HTMLElement | null>(null)
46
- const [popover, setPopover] = useState<HTMLElement | null>(null)
47
- const handleClickOutside = useCallback(() => setOpen(false), [])
48
- useClickOutside(handleClickOutside, [button, popover])
49
-
50
- // Get metadata from content lake
51
- const {data, loading, error} = useTranslationMetadata(documentId)
52
- const metadata = Array.isArray(data) && data.length ? data[0] : null
53
-
54
- // Optimistically set a metadata ID for a newly created metadata document
55
- // Cannot rely on generated metadata._id from useTranslationMetadata
56
- // As the document store might not have returned it before creating another translation
57
- const metadataId = useMemo(() => {
58
- if (loading) {
59
- return null
60
- }
61
-
62
- // Once created, these two values should be the same anyway
63
- return metadata?._id ?? uuid()
64
- }, [loading, metadata?._id])
65
-
66
- // Duplicate a new language version from the most recent version of this document
67
- const {draft, published} = useEditState(documentId, schemaType.name)
68
- const source = draft || published
69
-
70
- // Check for data issues
71
- const documentIsInOneMetadataDocument = useMemo(() => {
72
- return Array.isArray(data) && data.length <= 1
73
- }, [data])
74
- const sourceLanguageId = source?.[languageField] as string | undefined
75
- const sourceLanguageIsValid = supportedLanguages.some(
76
- (l) => l.id === sourceLanguageId
77
- )
78
- const allLanguagesAreValid = useMemo(() => {
79
- const valid = supportedLanguages.every((l) => l.id && l.title)
80
- if (!valid) {
81
- console.warn(
82
- `Not all languages are valid. It should be an array of objects with an "id" and "title" property. Or a function that returns an array of objects with an "id" and "title" property.`,
83
- supportedLanguages
84
- )
85
- }
86
-
87
- return valid
88
- }, [supportedLanguages])
89
-
90
- const content = (
91
- <Box padding={1}>
92
- {error ? (
93
- <Card tone="critical" padding={1}>
94
- <Text>There was an error returning translations metadata</Text>
95
- </Card>
96
- ) : (
97
- <Stack space={1}>
98
- <LanguageManage
99
- id={metadata?._id}
100
- documentId={documentId}
101
- metadataId={metadataId}
102
- schemaType={schemaType}
103
- sourceLanguageId={sourceLanguageId}
104
- />
105
- {supportedLanguages.length > 4 ? (
106
- <TextInput
107
- onChange={handleQuery}
108
- value={query}
109
- placeholder="Filter languages"
110
- />
111
- ) : null}
112
- {supportedLanguages.length > 0 ? (
113
- <>
114
- {/* Once metadata is loaded, there may be issues */}
115
- {loading ? null : (
116
- <>
117
- {/* Not all languages are valid */}
118
- {data && documentIsInOneMetadataDocument ? null : (
119
- <Warning>
120
- {/* TODO: Surface these documents to the user */}
121
- This document has been found in more than one Translations
122
- Metadata documents
123
- </Warning>
124
- )}
125
- {/* Not all languages are valid */}
126
- {allLanguagesAreValid ? null : (
127
- <Warning>
128
- Not all language objects are valid. See the console.
129
- </Warning>
130
- )}
131
- {/* Current document has no language field */}
132
- {sourceLanguageId ? null : (
133
- <Warning>
134
- Choose a language to apply to{' '}
135
- <strong>this Document</strong>
136
- </Warning>
137
- )}
138
- {/* Current document has an invalid language field */}
139
- {sourceLanguageId && !sourceLanguageIsValid ? (
140
- <Warning>
141
- Select a supported language. Current language value:{' '}
142
- <code>{sourceLanguageId}</code>
143
- </Warning>
144
- ) : null}
145
- </>
146
- )}
147
- {supportedLanguages
148
- .filter((language) => {
149
- if (query) {
150
- return language.title
151
- .toLowerCase()
152
- .includes(query.toLowerCase())
153
- }
154
- return true
155
- })
156
- .map((language) =>
157
- !loading && sourceLanguageId && sourceLanguageIsValid ? (
158
- // Button to duplicate this document to a new translation
159
- // And either create or update the metadata document
160
- <LanguageOption
161
- key={language.id}
162
- language={language}
163
- schemaType={schemaType}
164
- documentId={documentId}
165
- disabled={loading || !allLanguagesAreValid}
166
- current={language.id === sourceLanguageId}
167
- metadata={metadata}
168
- metadataId={metadataId}
169
- source={source}
170
- sourceLanguageId={sourceLanguageId}
171
- />
172
- ) : (
173
- // Button to set a language field on *this* document
174
- <LanguagePatch
175
- key={language.id}
176
- source={source}
177
- language={language}
178
- // Only allow language patch change to:
179
- // - Keys not in metadata
180
- // - The key of this document in the metadata
181
- disabled={
182
- (loading ||
183
- !allLanguagesAreValid ||
184
- metadata?.translations
185
- .filter((t) => t?.value?._ref !== documentId)
186
- .some((t) => t._key === language.id)) ??
187
- false
188
- }
189
- />
190
- )
191
- )}
192
- </>
193
- ) : null}
194
- </Stack>
195
- )}
196
- </Box>
197
- )
198
-
199
- const issueWithTranslations =
200
- !loading && sourceLanguageId && !sourceLanguageIsValid
201
-
202
- if (!documentId) {
203
- return null
204
- }
205
-
206
- if (!schemaType || !schemaType.name) {
207
- return null
208
- }
209
-
210
- return (
211
- <Popover
212
- animate
213
- constrainSize
214
- content={content}
215
- open={open}
216
- portal
217
- ref={setPopover}
218
- overflow="auto"
219
- tone="default"
220
- >
221
- <Button
222
- text="Translations"
223
- mode="bleed"
224
- disabled={!source}
225
- tone={
226
- !source || loading || !issueWithTranslations ? undefined : `caution`
227
- }
228
- icon={TranslateIcon}
229
- onClick={handleClick}
230
- ref={setButton}
231
- selected={open}
232
- />
233
- </Popover>
234
- )
235
- }
@@ -1,108 +0,0 @@
1
- import {CogIcon} from '@sanity/icons'
2
- import {Box, Button, Stack, Text, Tooltip} from '@sanity/ui'
3
- import {useCallback, useState} from 'react'
4
- import {type ObjectSchemaType, useClient} from 'sanity'
5
-
6
- import {METADATA_SCHEMA_NAME} from '../constants'
7
- import {useOpenInNewPane} from '../hooks/useOpenInNewPane'
8
- import {createReference} from '../utils/createReference'
9
- import {useDocumentInternationalizationContext} from './DocumentInternationalizationContext'
10
-
11
- type LanguageManageProps = {
12
- id?: string
13
- metadataId?: string | null
14
- schemaType: ObjectSchemaType
15
- documentId: string
16
- sourceLanguageId?: string
17
- }
18
-
19
- export default function LanguageManage(props: LanguageManageProps) {
20
- const {id, metadataId, schemaType, documentId, sourceLanguageId} = props
21
- const open = useOpenInNewPane(id, METADATA_SCHEMA_NAME)
22
- const openCreated = useOpenInNewPane(metadataId, METADATA_SCHEMA_NAME)
23
- const {allowCreateMetaDoc, apiVersion, weakReferences} =
24
- useDocumentInternationalizationContext()
25
- const client = useClient({apiVersion})
26
- const [userHasClicked, setUserHasClicked] = useState(false)
27
-
28
- const canCreate = !id && Boolean(metadataId) && allowCreateMetaDoc
29
-
30
- const handleClick = useCallback(() => {
31
- if (!id && metadataId && sourceLanguageId) {
32
- /* Disable button while this request is pending */
33
- setUserHasClicked(true)
34
-
35
- // handle creation of meta document
36
- const transaction = client.transaction()
37
-
38
- const sourceReference = createReference(
39
- sourceLanguageId,
40
- documentId,
41
- schemaType.name,
42
- !weakReferences
43
- )
44
- const newMetadataDocument = {
45
- _id: metadataId,
46
- _type: METADATA_SCHEMA_NAME,
47
- schemaTypes: [schemaType.name],
48
- translations: [sourceReference],
49
- }
50
-
51
- transaction.createIfNotExists(newMetadataDocument)
52
-
53
- transaction
54
- .commit()
55
- .then(() => {
56
- setUserHasClicked(false)
57
- openCreated()
58
- })
59
- .catch((err) => {
60
- console.error(err)
61
- setUserHasClicked(false)
62
- })
63
- } else {
64
- open()
65
- }
66
- }, [
67
- id,
68
- metadataId,
69
- sourceLanguageId,
70
- client,
71
- documentId,
72
- schemaType.name,
73
- weakReferences,
74
- openCreated,
75
- open,
76
- ])
77
-
78
- const disabled =
79
- (!id && !canCreate) || (canCreate && !sourceLanguageId) || userHasClicked
80
-
81
- return (
82
- <Tooltip
83
- animate
84
- content={
85
- <Box padding={2}>
86
- <Text muted size={1}>
87
- Document has no other translations
88
- </Text>
89
- </Box>
90
- }
91
- fallbackPlacements={['right', 'left']}
92
- placement="top"
93
- portal
94
- disabled={Boolean(id) || canCreate}
95
- >
96
- <Stack>
97
- <Button
98
- disabled={disabled}
99
- mode="ghost"
100
- text="Manage Translations"
101
- icon={CogIcon}
102
- loading={userHasClicked}
103
- onClick={handleClick}
104
- />
105
- </Stack>
106
- </Tooltip>
107
- )
108
- }