sanity-plugin-internationalized-array 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -7
- package/lib/index.d.ts +10 -10
- package/lib/index.esm.js +19 -13
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +19 -13
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/InternationalizedArray.tsx +94 -172
- package/src/components/InternationalizedArrayContext.tsx +91 -0
- package/src/components/InternationalizedInput.tsx +3 -3
- package/src/constants.ts +12 -0
- package/src/fieldActions/index.ts +132 -0
- package/src/plugin.tsx +73 -40
- package/src/schema/array.ts +3 -2
- package/src/types.ts +10 -10
- package/src/utils/checkAllLanguagesArePresent.ts +14 -0
- package/src/utils/createAddAllTitle.ts +16 -0
- package/src/utils/createAddLanguagePatches.ts +75 -0
- package/src/components/languageContext.tsx +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-internationalized-array",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Store localized fields in an array to save on attributes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"react-dom": "^18",
|
|
84
84
|
"react-is": "^18",
|
|
85
85
|
"rimraf": "^4.1.2",
|
|
86
|
-
"sanity": "^3.
|
|
86
|
+
"sanity": "^3.14.1",
|
|
87
87
|
"semantic-release": "^20.1.0",
|
|
88
88
|
"typescript": "^4.9.5"
|
|
89
89
|
},
|
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
import {AddIcon} from '@sanity/icons'
|
|
2
2
|
import {useLanguageFilterStudioContext} from '@sanity/language-filter'
|
|
3
3
|
import {Button, Grid, Stack, useToast} from '@sanity/ui'
|
|
4
|
-
import
|
|
5
|
-
import React, {useCallback, useDeferredValue, useEffect, useMemo} from 'react'
|
|
4
|
+
import React, {useCallback, useEffect, useMemo} from 'react'
|
|
6
5
|
import {
|
|
7
6
|
ArrayOfObjectsInputProps,
|
|
8
7
|
ArrayOfObjectsItem,
|
|
9
|
-
|
|
8
|
+
ArraySchemaType,
|
|
10
9
|
set,
|
|
11
10
|
setIfMissing,
|
|
12
|
-
useClient,
|
|
13
|
-
useFormBuilder,
|
|
14
11
|
useFormValue,
|
|
15
12
|
} from 'sanity'
|
|
16
|
-
import {suspend} from 'suspend-react'
|
|
17
13
|
|
|
18
|
-
import {namespace, version} from '../cache'
|
|
19
14
|
import {MAX_COLUMNS} from '../constants'
|
|
20
|
-
import type {
|
|
15
|
+
import type {Value} from '../types'
|
|
16
|
+
import {checkAllLanguagesArePresent} from '../utils/checkAllLanguagesArePresent'
|
|
17
|
+
import {createAddAllTitle} from '../utils/createAddAllTitle'
|
|
18
|
+
import {createAddLanguagePatches} from '../utils/createAddLanguagePatches'
|
|
21
19
|
import Feedback from './Feedback'
|
|
22
|
-
import {
|
|
23
|
-
// TODO: Move this provider to the root component
|
|
24
|
-
import {LanguageProvider} from './languageContext'
|
|
20
|
+
import {useInternationalizedArrayContext} from './InternationalizedArrayContext'
|
|
25
21
|
|
|
26
22
|
export type InternationalizedArrayProps = ArrayOfObjectsInputProps<
|
|
27
23
|
Value,
|
|
28
|
-
|
|
24
|
+
ArraySchemaType
|
|
29
25
|
>
|
|
30
26
|
|
|
31
27
|
export default function InternationalizedArray(
|
|
@@ -35,30 +31,15 @@ export default function InternationalizedArray(
|
|
|
35
31
|
|
|
36
32
|
const readOnly =
|
|
37
33
|
typeof schemaType.readOnly === 'boolean' ? schemaType.readOnly : false
|
|
38
|
-
const {options} = schemaType
|
|
39
34
|
const toast = useToast()
|
|
40
|
-
const {value: document} = useFormBuilder()
|
|
41
|
-
const deferredDocument = useDeferredValue(document)
|
|
42
|
-
const selectedValue = useMemo(
|
|
43
|
-
() => getSelectedValue(options.select, deferredDocument),
|
|
44
|
-
[options.select, deferredDocument]
|
|
45
|
-
)
|
|
46
35
|
|
|
47
|
-
const {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (typeof options.languages === 'function') {
|
|
55
|
-
return options.languages(client, selectedValue)
|
|
56
|
-
}
|
|
57
|
-
return options.languages
|
|
58
|
-
},
|
|
59
|
-
[version, namespace, selectedValue],
|
|
60
|
-
{equal}
|
|
61
|
-
)
|
|
36
|
+
const {
|
|
37
|
+
languages,
|
|
38
|
+
filteredLanguages,
|
|
39
|
+
defaultLanguages,
|
|
40
|
+
buttonAddAll,
|
|
41
|
+
buttonLocations,
|
|
42
|
+
} = useInternationalizedArrayContext()
|
|
62
43
|
|
|
63
44
|
// Support updating the UI if languageFilter is installed
|
|
64
45
|
const {selectedLanguageIds, options: languageFilterOptions} =
|
|
@@ -96,74 +77,27 @@ export default function InternationalizedArray(
|
|
|
96
77
|
[languageFilterEnabled, members, languageFilterOptions, selectedLanguageIds]
|
|
97
78
|
)
|
|
98
79
|
|
|
99
|
-
const filteredLanguages = useMemo(
|
|
100
|
-
() =>
|
|
101
|
-
languageFilterEnabled
|
|
102
|
-
? languages.filter((language) =>
|
|
103
|
-
selectedLanguageIds.includes(language.id)
|
|
104
|
-
)
|
|
105
|
-
: languages,
|
|
106
|
-
[languageFilterEnabled, languages, selectedLanguageIds]
|
|
107
|
-
)
|
|
108
|
-
|
|
109
80
|
const handleAddLanguage = useCallback(
|
|
110
81
|
(param?: React.MouseEvent<HTMLButtonElement, MouseEvent> | string[]) => {
|
|
111
82
|
if (!filteredLanguages?.length) {
|
|
112
83
|
return
|
|
113
84
|
}
|
|
114
85
|
|
|
115
|
-
const
|
|
86
|
+
const addLanguageKeys: string[] = Array.isArray(param)
|
|
116
87
|
? param
|
|
117
88
|
: ([param?.currentTarget?.value].filter(Boolean) as string[])
|
|
118
|
-
const itemBase = {_type: `${schemaType.name}Value`}
|
|
119
|
-
|
|
120
|
-
// Create new items
|
|
121
|
-
const newItems =
|
|
122
|
-
Array.isArray(languageIds) && languageIds.length > 0
|
|
123
|
-
? // Just one for this language
|
|
124
|
-
languageIds.map((id) => ({...itemBase, _key: id}))
|
|
125
|
-
: // Or one for every missing language
|
|
126
|
-
filteredLanguages
|
|
127
|
-
.filter((language) =>
|
|
128
|
-
value?.length
|
|
129
|
-
? !value.find((v) => v._key === language.id)
|
|
130
|
-
: true
|
|
131
|
-
)
|
|
132
|
-
.map((language) => ({...itemBase, _key: language.id}))
|
|
133
|
-
|
|
134
|
-
// Insert new items in the correct order
|
|
135
|
-
const languagesInUse = value?.length ? value.map((v) => v) : []
|
|
136
|
-
|
|
137
|
-
const insertions = newItems.map((item) => {
|
|
138
|
-
// What's the original index of this language?
|
|
139
|
-
const languageIndex = languages.findIndex((l) => item._key === l.id)
|
|
140
|
-
|
|
141
|
-
// What languages are there beyond that index?
|
|
142
|
-
const remainingLanguages = languages.slice(languageIndex + 1)
|
|
143
89
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// Keep local state up to date incase multiple insertions are being made
|
|
151
|
-
if (nextLanguageIndex < 0) {
|
|
152
|
-
languagesInUse.push(item)
|
|
153
|
-
} else {
|
|
154
|
-
languagesInUse.splice(nextLanguageIndex, 0, item)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return nextLanguageIndex < 0
|
|
158
|
-
? // No next language (-1), add to end of array
|
|
159
|
-
insert([item], 'after', [nextLanguageIndex])
|
|
160
|
-
: // Next language found, insert before that
|
|
161
|
-
insert([item], 'before', [nextLanguageIndex])
|
|
90
|
+
const patches = createAddLanguagePatches({
|
|
91
|
+
addLanguageKeys,
|
|
92
|
+
schemaType,
|
|
93
|
+
languages,
|
|
94
|
+
filteredLanguages,
|
|
95
|
+
value,
|
|
162
96
|
})
|
|
163
97
|
|
|
164
|
-
onChange([setIfMissing([]), ...
|
|
98
|
+
onChange([setIfMissing([]), ...patches])
|
|
165
99
|
},
|
|
166
|
-
[filteredLanguages, onChange, schemaType
|
|
100
|
+
[filteredLanguages, languages, onChange, schemaType, value]
|
|
167
101
|
)
|
|
168
102
|
|
|
169
103
|
// Create default fields if the document is not yet created
|
|
@@ -255,100 +189,88 @@ export default function InternationalizedArray(
|
|
|
255
189
|
}, [languagesOutOfOrder, allKeysAreLanguages, handleRestoreOrder])
|
|
256
190
|
|
|
257
191
|
// compare value keys with possible languages
|
|
258
|
-
const allLanguagesArePresent = useMemo(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return (
|
|
263
|
-
languagesInUseIds.length === filteredLanguageIds.length &&
|
|
264
|
-
languagesInUseIds.every((l) => filteredLanguageIds.includes(l))
|
|
265
|
-
)
|
|
266
|
-
}, [filteredLanguages, value])
|
|
192
|
+
const allLanguagesArePresent = useMemo(
|
|
193
|
+
() => checkAllLanguagesArePresent(filteredLanguages, value),
|
|
194
|
+
[filteredLanguages, value]
|
|
195
|
+
)
|
|
267
196
|
|
|
268
197
|
if (!languagesAreValid) {
|
|
269
198
|
return <Feedback />
|
|
270
199
|
}
|
|
271
200
|
|
|
201
|
+
const addButtonsAreVisible =
|
|
202
|
+
// Plugin was configured to display buttons here (default!)
|
|
203
|
+
buttonLocations.includes('field') &&
|
|
204
|
+
// There's at least one language visible
|
|
205
|
+
filteredLanguages?.length > 0 &&
|
|
206
|
+
// Not every language has a value yet
|
|
207
|
+
!allLanguagesArePresent
|
|
208
|
+
|
|
272
209
|
return (
|
|
273
|
-
<
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
{
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
/>
|
|
327
|
-
))}
|
|
328
|
-
</Grid>
|
|
329
|
-
) : null}
|
|
210
|
+
<Stack space={2}>
|
|
211
|
+
{members?.length > 0 ? (
|
|
212
|
+
<>
|
|
213
|
+
{filteredMembers.map((member) => {
|
|
214
|
+
if (member.kind === 'item') {
|
|
215
|
+
return (
|
|
216
|
+
<ArrayOfObjectsItem
|
|
217
|
+
key={member.key}
|
|
218
|
+
member={member}
|
|
219
|
+
renderItem={props.renderItem}
|
|
220
|
+
renderField={props.renderField}
|
|
221
|
+
renderInput={props.renderInput}
|
|
222
|
+
renderPreview={props.renderPreview}
|
|
223
|
+
/>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return null
|
|
228
|
+
})}
|
|
229
|
+
</>
|
|
230
|
+
) : null}
|
|
231
|
+
|
|
232
|
+
{addButtonsAreVisible ? (
|
|
233
|
+
<Stack space={2}>
|
|
234
|
+
{/* Hide language-specific buttons if there's only one */}
|
|
235
|
+
{/* No more than 7 columns */}
|
|
236
|
+
{filteredLanguages.length > 1 ? (
|
|
237
|
+
<Grid
|
|
238
|
+
columns={Math.min(filteredLanguages.length, MAX_COLUMNS)}
|
|
239
|
+
gap={2}
|
|
240
|
+
>
|
|
241
|
+
{filteredLanguages.map((language) => (
|
|
242
|
+
<Button
|
|
243
|
+
key={language.id}
|
|
244
|
+
tone="primary"
|
|
245
|
+
mode="ghost"
|
|
246
|
+
fontSize={1}
|
|
247
|
+
disabled={
|
|
248
|
+
readOnly ||
|
|
249
|
+
Boolean(value?.find((item) => item._key === language.id))
|
|
250
|
+
}
|
|
251
|
+
text={language.id.toUpperCase()}
|
|
252
|
+
// Only show plus icon if there's one row or less
|
|
253
|
+
icon={
|
|
254
|
+
filteredLanguages.length > MAX_COLUMNS ? undefined : AddIcon
|
|
255
|
+
}
|
|
256
|
+
value={language.id}
|
|
257
|
+
onClick={handleAddLanguage}
|
|
258
|
+
/>
|
|
259
|
+
))}
|
|
260
|
+
</Grid>
|
|
261
|
+
) : null}
|
|
262
|
+
{buttonAddAll ? (
|
|
330
263
|
<Button
|
|
331
264
|
tone="primary"
|
|
332
265
|
mode="ghost"
|
|
333
266
|
disabled={readOnly || allLanguagesArePresent}
|
|
334
267
|
icon={AddIcon}
|
|
335
|
-
text={
|
|
336
|
-
// eslint-disable-next-line no-nested-ternary
|
|
337
|
-
value?.length
|
|
338
|
-
? `Add missing ${
|
|
339
|
-
filteredLanguages.length - value.length === 1
|
|
340
|
-
? `language`
|
|
341
|
-
: `languages`
|
|
342
|
-
}`
|
|
343
|
-
: filteredLanguages.length === 1
|
|
344
|
-
? `Add ${filteredLanguages[0].title} Field`
|
|
345
|
-
: `Add all languages`
|
|
346
|
-
}
|
|
268
|
+
text={createAddAllTitle(value, filteredLanguages)}
|
|
347
269
|
onClick={handleAddLanguage}
|
|
348
270
|
/>
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
</
|
|
271
|
+
) : null}
|
|
272
|
+
</Stack>
|
|
273
|
+
) : null}
|
|
274
|
+
</Stack>
|
|
353
275
|
)
|
|
354
276
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {useLanguageFilterStudioContext} from '@sanity/language-filter'
|
|
2
|
+
import equal from 'fast-deep-equal'
|
|
3
|
+
import {createContext, useContext, useDeferredValue, useMemo} from 'react'
|
|
4
|
+
import {ObjectInputProps, useClient, useFormBuilder} from 'sanity'
|
|
5
|
+
import {suspend} from 'suspend-react'
|
|
6
|
+
|
|
7
|
+
import {namespace, version} from '../cache'
|
|
8
|
+
import {CONFIG_DEFAULT} from '../constants'
|
|
9
|
+
import {Language, PluginConfig} from '../types'
|
|
10
|
+
import {getSelectedValue} from './getSelectedValue'
|
|
11
|
+
|
|
12
|
+
// This provider makes the plugin config available to all components in the document form
|
|
13
|
+
// But with languages resolved and filtered languages updated base on @sanity/language-filter
|
|
14
|
+
|
|
15
|
+
type InternationalizedArrayContextProps = Required<PluginConfig> & {
|
|
16
|
+
languages: Language[]
|
|
17
|
+
filteredLanguages: Language[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const InternationalizedArrayContext =
|
|
21
|
+
createContext<InternationalizedArrayContextProps>({
|
|
22
|
+
...CONFIG_DEFAULT,
|
|
23
|
+
languages: [],
|
|
24
|
+
filteredLanguages: [],
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export function useInternationalizedArrayContext() {
|
|
28
|
+
return useContext(InternationalizedArrayContext)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type InternationalizedArrayProviderProps = ObjectInputProps & {
|
|
32
|
+
internationalizedArray: Required<PluginConfig>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function InternationalizedArrayProvider(
|
|
36
|
+
props: InternationalizedArrayProviderProps
|
|
37
|
+
) {
|
|
38
|
+
const {internationalizedArray} = props
|
|
39
|
+
|
|
40
|
+
const client = useClient({apiVersion: internationalizedArray.apiVersion})
|
|
41
|
+
const {value: document} = useFormBuilder()
|
|
42
|
+
const deferredDocument = useDeferredValue(document)
|
|
43
|
+
const selectedValue = useMemo(
|
|
44
|
+
() => getSelectedValue(internationalizedArray.select, deferredDocument),
|
|
45
|
+
[internationalizedArray.select, deferredDocument]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
// Fetch or return languages
|
|
49
|
+
const languages = Array.isArray(internationalizedArray.languages)
|
|
50
|
+
? internationalizedArray.languages
|
|
51
|
+
: suspend(
|
|
52
|
+
// eslint-disable-next-line require-await
|
|
53
|
+
async () => {
|
|
54
|
+
if (typeof internationalizedArray.languages === 'function') {
|
|
55
|
+
return internationalizedArray.languages(client, selectedValue)
|
|
56
|
+
}
|
|
57
|
+
return internationalizedArray.languages
|
|
58
|
+
},
|
|
59
|
+
[version, namespace],
|
|
60
|
+
{equal}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
// Filter out some languages if language filter is enabled
|
|
64
|
+
const {selectedLanguageIds, options: languageFilterOptions} =
|
|
65
|
+
useLanguageFilterStudioContext()
|
|
66
|
+
|
|
67
|
+
const filteredLanguages = useMemo(() => {
|
|
68
|
+
const documentType = deferredDocument ? deferredDocument._type : undefined
|
|
69
|
+
const languageFilterEnabled =
|
|
70
|
+
typeof documentType === 'string' &&
|
|
71
|
+
languageFilterOptions.documentTypes.includes(documentType)
|
|
72
|
+
|
|
73
|
+
return languageFilterEnabled
|
|
74
|
+
? languages.filter((language) =>
|
|
75
|
+
selectedLanguageIds.includes(language.id)
|
|
76
|
+
)
|
|
77
|
+
: languages
|
|
78
|
+
}, [deferredDocument, languageFilterOptions, languages, selectedLanguageIds])
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<InternationalizedArrayContext.Provider
|
|
82
|
+
value={{
|
|
83
|
+
...internationalizedArray,
|
|
84
|
+
languages,
|
|
85
|
+
filteredLanguages,
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
{props.renderDefault(props)}
|
|
89
|
+
</InternationalizedArrayContext.Provider>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
Spinner,
|
|
11
11
|
Stack,
|
|
12
12
|
} from '@sanity/ui'
|
|
13
|
-
import React, {useCallback,
|
|
13
|
+
import React, {useCallback, useMemo} from 'react'
|
|
14
14
|
import {ObjectItemProps, useFormValue} from 'sanity'
|
|
15
15
|
import {set, unset} from 'sanity'
|
|
16
16
|
|
|
17
17
|
import {getToneFromValidation} from './getToneFromValidation'
|
|
18
|
-
import {
|
|
18
|
+
import {useInternationalizedArrayContext} from './InternationalizedArrayContext'
|
|
19
19
|
|
|
20
20
|
type InternationalizedValue = {
|
|
21
21
|
_type: string
|
|
@@ -44,7 +44,7 @@ export default function InternationalizedInput(
|
|
|
44
44
|
const {validation, value, onChange, readOnly} = inlineProps
|
|
45
45
|
|
|
46
46
|
// The parent array contains the languages from the plugin config
|
|
47
|
-
const {languages} =
|
|
47
|
+
const {languages} = useInternationalizedArrayContext()
|
|
48
48
|
|
|
49
49
|
const languageKeysInUse = useMemo(
|
|
50
50
|
() => parentValue?.map((v) => v._key) ?? [],
|
package/src/constants.ts
CHANGED
|
@@ -1 +1,13 @@
|
|
|
1
|
+
import {PluginConfig} from './types'
|
|
2
|
+
|
|
1
3
|
export const MAX_COLUMNS = 7
|
|
4
|
+
|
|
5
|
+
export const CONFIG_DEFAULT: Required<PluginConfig> = {
|
|
6
|
+
languages: [],
|
|
7
|
+
select: {},
|
|
8
|
+
defaultLanguages: [],
|
|
9
|
+
fieldTypes: [],
|
|
10
|
+
apiVersion: '2022-11-27',
|
|
11
|
+
buttonLocations: ['field'],
|
|
12
|
+
buttonAddAll: true,
|
|
13
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {AddIcon, TranslateIcon} from '@sanity/icons'
|
|
2
|
+
import {useCallback} from 'react'
|
|
3
|
+
import {
|
|
4
|
+
defineDocumentFieldAction,
|
|
5
|
+
DocumentFieldActionItem,
|
|
6
|
+
DocumentFieldActionProps,
|
|
7
|
+
PatchEvent,
|
|
8
|
+
setIfMissing,
|
|
9
|
+
useFormValue,
|
|
10
|
+
} from 'sanity'
|
|
11
|
+
import {useDocumentPane} from 'sanity/desk'
|
|
12
|
+
|
|
13
|
+
import {useInternationalizedArrayContext} from '../components/InternationalizedArrayContext'
|
|
14
|
+
import {Language, Value} from '../types'
|
|
15
|
+
import {checkAllLanguagesArePresent} from '../utils/checkAllLanguagesArePresent'
|
|
16
|
+
import {createAddAllTitle} from '../utils/createAddAllTitle'
|
|
17
|
+
import {createAddLanguagePatches} from '../utils/createAddLanguagePatches'
|
|
18
|
+
|
|
19
|
+
const createTranslateFieldActions: (
|
|
20
|
+
fieldActionProps: DocumentFieldActionProps,
|
|
21
|
+
context: {languages: Language[]; filteredLanguages: Language[]}
|
|
22
|
+
) => DocumentFieldActionItem[] = (
|
|
23
|
+
fieldActionProps,
|
|
24
|
+
{languages, filteredLanguages}
|
|
25
|
+
) =>
|
|
26
|
+
languages.map((language) => {
|
|
27
|
+
const value = useFormValue(fieldActionProps.path) as Value[]
|
|
28
|
+
const disabled =
|
|
29
|
+
value && Array.isArray(value)
|
|
30
|
+
? Boolean(value?.find((item) => item._key === language.id))
|
|
31
|
+
: true
|
|
32
|
+
const hidden = !filteredLanguages.some((f) => f.id === language.id)
|
|
33
|
+
|
|
34
|
+
const {onChange} = useDocumentPane()
|
|
35
|
+
|
|
36
|
+
const onAction = useCallback(() => {
|
|
37
|
+
const {schemaType, path} = fieldActionProps
|
|
38
|
+
|
|
39
|
+
const addLanguageKeys = [language.id]
|
|
40
|
+
const patches = createAddLanguagePatches({
|
|
41
|
+
addLanguageKeys,
|
|
42
|
+
schemaType,
|
|
43
|
+
languages,
|
|
44
|
+
filteredLanguages,
|
|
45
|
+
value,
|
|
46
|
+
path,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
onChange(PatchEvent.from([setIfMissing([], path), ...patches]))
|
|
50
|
+
}, [language.id, value, onChange])
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
type: 'action',
|
|
54
|
+
icon: AddIcon,
|
|
55
|
+
onAction,
|
|
56
|
+
title: language.id.toLocaleUpperCase(),
|
|
57
|
+
hidden,
|
|
58
|
+
disabled,
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const AddMissingTranslationsFieldAction: (
|
|
63
|
+
fieldActionProps: DocumentFieldActionProps,
|
|
64
|
+
context: {languages: Language[]; filteredLanguages: Language[]}
|
|
65
|
+
) => DocumentFieldActionItem = (
|
|
66
|
+
fieldActionProps,
|
|
67
|
+
{languages, filteredLanguages}
|
|
68
|
+
) => {
|
|
69
|
+
const value = useFormValue(fieldActionProps.path) as Value[]
|
|
70
|
+
const disabled = value.length === filteredLanguages.length
|
|
71
|
+
const hidden = checkAllLanguagesArePresent(filteredLanguages, value)
|
|
72
|
+
|
|
73
|
+
const {onChange} = useDocumentPane()
|
|
74
|
+
|
|
75
|
+
const onAction = useCallback(() => {
|
|
76
|
+
const {schemaType, path} = fieldActionProps
|
|
77
|
+
|
|
78
|
+
const addLanguageKeys: string[] = []
|
|
79
|
+
const patches = createAddLanguagePatches({
|
|
80
|
+
addLanguageKeys,
|
|
81
|
+
schemaType,
|
|
82
|
+
languages,
|
|
83
|
+
filteredLanguages,
|
|
84
|
+
value,
|
|
85
|
+
path,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
onChange(PatchEvent.from([setIfMissing([], path), ...patches]))
|
|
89
|
+
}, [fieldActionProps, filteredLanguages, languages, onChange, value])
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
type: 'action',
|
|
93
|
+
icon: AddIcon,
|
|
94
|
+
onAction,
|
|
95
|
+
title: createAddAllTitle(value, filteredLanguages),
|
|
96
|
+
disabled,
|
|
97
|
+
hidden,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const internationalizedArrayFieldAction = defineDocumentFieldAction({
|
|
102
|
+
name: 'internationalizedArray',
|
|
103
|
+
useAction(fieldActionProps) {
|
|
104
|
+
const isInternationalizedArrayField =
|
|
105
|
+
fieldActionProps?.schemaType?.type?.name.startsWith(
|
|
106
|
+
'internationalizedArray'
|
|
107
|
+
)
|
|
108
|
+
const {languages, filteredLanguages} = useInternationalizedArrayContext()
|
|
109
|
+
|
|
110
|
+
const translateFieldActions = createTranslateFieldActions(
|
|
111
|
+
fieldActionProps,
|
|
112
|
+
{languages, filteredLanguages}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
type: 'group',
|
|
117
|
+
icon: TranslateIcon,
|
|
118
|
+
title: 'Add Translation',
|
|
119
|
+
renderAsButton: true,
|
|
120
|
+
children: isInternationalizedArrayField
|
|
121
|
+
? [
|
|
122
|
+
...translateFieldActions,
|
|
123
|
+
AddMissingTranslationsFieldAction(fieldActionProps, {
|
|
124
|
+
languages,
|
|
125
|
+
filteredLanguages,
|
|
126
|
+
}),
|
|
127
|
+
]
|
|
128
|
+
: [],
|
|
129
|
+
hidden: !isInternationalizedArrayField,
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
})
|