@sanity/document-internationalization 2.1.2 → 3.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 (34) hide show
  1. package/dist/index.d.mts +90 -0
  2. package/dist/index.d.ts +3 -1
  3. package/dist/index.esm.js +768 -1166
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.js +753 -1176
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +1048 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +34 -38
  10. package/src/actions/DeleteMetadataAction.tsx +5 -5
  11. package/src/actions/DeleteTranslationAction.tsx +6 -2
  12. package/src/badges/index.tsx +1 -1
  13. package/src/components/BulkPublish/DocumentCheck.tsx +1 -1
  14. package/src/components/BulkPublish/InfoIcon.tsx +4 -3
  15. package/src/components/BulkPublish/index.tsx +3 -2
  16. package/src/components/ConstrainedBox.tsx +1 -1
  17. package/src/components/DeleteTranslationDialog/index.tsx +1 -1
  18. package/src/components/DeleteTranslationDialog/separateReferences.ts +1 -1
  19. package/src/components/DocumentInternationalizationContext.tsx +2 -2
  20. package/src/components/DocumentInternationalizationMenu.tsx +3 -2
  21. package/src/components/LanguageManage.tsx +1 -0
  22. package/src/components/LanguageOption.tsx +28 -4
  23. package/src/components/LanguagePatch.tsx +2 -2
  24. package/src/components/OptimisticallyStrengthen/ReferencePatcher.tsx +2 -2
  25. package/src/components/OptimisticallyStrengthen/index.tsx +1 -1
  26. package/src/components/Warning.tsx +1 -1
  27. package/src/constants.ts +1 -1
  28. package/src/hooks/useLanguageMetadata.tsx +1 -1
  29. package/src/hooks/useOpenInNewPane.tsx +2 -2
  30. package/src/plugin.tsx +4 -4
  31. package/src/schema/translation/metadata.ts +2 -4
  32. package/src/utils/createReference.ts +1 -1
  33. package/src/utils/excludePaths.ts +4 -4
  34. package/dist/index.cjs.mjs +0 -7
package/dist/index.mjs ADDED
@@ -0,0 +1,1048 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { TrashIcon, CogIcon, SplitVerticalIcon, CheckmarkIcon, AddIcon, EditIcon, TranslateIcon, InfoOutlineIcon } from "@sanity/icons";
3
+ import { Flex, Spinner, Stack, Text, Card, Grid, Button, useToast, Tooltip, Box, Badge, useClickOutside, TextInput, Popover, Inline, Dialog } from "@sanity/ui";
4
+ import { useMemo, useEffect, createContext, useContext, useState, useCallback } from "react";
5
+ import { useSchema, Preview, useClient, useWorkspace, isDocumentSchemaType, pathToString, useEditState, useValidationStatus, TextWithTone, PatchEvent, unset, defineType, defineField, definePlugin, isSanityDocument } from "sanity";
6
+ import { Feedback, useListeningQuery } from "sanity-plugin-utils";
7
+ import { uuid } from "@sanity/uuid";
8
+ import { RouterContext } from "sanity/router";
9
+ import { usePaneRouter, useDocumentPane } from "sanity/structure";
10
+ import { Mutation, extractWithPath } from "@sanity/mutator";
11
+ import { styled } from "styled-components";
12
+ import { internationalizedArray } from "sanity-plugin-internationalized-array";
13
+ function DocumentPreview(props) {
14
+ const schemaType = useSchema().get(props.type);
15
+ return schemaType ? /* @__PURE__ */ jsx(Preview, { value: props.value, schemaType }) : /* @__PURE__ */ jsx(Feedback, { tone: "critical", title: "Schema type not found" });
16
+ }
17
+ const METADATA_SCHEMA_NAME = "translation.metadata", TRANSLATIONS_ARRAY_NAME = "translations", API_VERSION = "2023-05-22", DEFAULT_CONFIG = {
18
+ supportedLanguages: [],
19
+ schemaTypes: [],
20
+ languageField: "language",
21
+ weakReferences: !1,
22
+ bulkPublish: !1,
23
+ metadataFields: [],
24
+ apiVersion: API_VERSION
25
+ };
26
+ function separateReferences(data = []) {
27
+ const translations = [], otherReferences = [];
28
+ return data && data.length > 0 && data.forEach((doc) => {
29
+ doc._type === METADATA_SCHEMA_NAME ? translations.push(doc) : otherReferences.push(doc);
30
+ }), { translations, otherReferences };
31
+ }
32
+ function DeleteTranslationDialog(props) {
33
+ const { doc, documentId, setTranslations } = props, { data, loading } = useListeningQuery(
34
+ "*[references($id)]{_id, _type}",
35
+ { params: { id: documentId }, initialValue: [] }
36
+ ), { translations, otherReferences } = useMemo(
37
+ () => separateReferences(data),
38
+ [data]
39
+ );
40
+ return useEffect(() => {
41
+ setTranslations(translations);
42
+ }, [setTranslations, translations]), loading ? /* @__PURE__ */ jsx(Flex, { padding: 4, align: "center", justify: "center", children: /* @__PURE__ */ jsx(Spinner, {}) }) : /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
43
+ translations && translations.length > 0 ? /* @__PURE__ */ jsx(Text, { children: "This document is a language-specific version which other translations depend on." }) : /* @__PURE__ */ jsx(Text, { children: "This document does not have connected translations." }),
44
+ /* @__PURE__ */ jsx(Card, { border: !0, padding: 3, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
45
+ /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: translations && translations.length > 0 ? /* @__PURE__ */ jsx(Fragment, { children: "Before this document can be deleted" }) : /* @__PURE__ */ jsx(Fragment, { children: "This document can now be deleted" }) }),
46
+ /* @__PURE__ */ jsx(DocumentPreview, { value: doc, type: doc._type }),
47
+ translations && translations.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
48
+ /* @__PURE__ */ jsx(Card, { borderTop: !0 }),
49
+ /* @__PURE__ */ jsxs(Text, { size: 1, weight: "semibold", children: [
50
+ "The reference in",
51
+ " ",
52
+ translations.length === 1 ? "this translations document" : "these translations documents",
53
+ " ",
54
+ "must be removed"
55
+ ] }),
56
+ translations.map((translation) => /* @__PURE__ */ jsx(
57
+ DocumentPreview,
58
+ {
59
+ value: translation,
60
+ type: translation._type
61
+ },
62
+ translation._id
63
+ ))
64
+ ] }) : null,
65
+ otherReferences && otherReferences.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
66
+ /* @__PURE__ */ jsx(Card, { borderTop: !0 }),
67
+ /* @__PURE__ */ jsxs(Text, { size: 1, weight: "semibold", children: [
68
+ otherReferences.length === 1 ? "There is an additional reference" : "There are additional references",
69
+ " ",
70
+ "to this document"
71
+ ] }),
72
+ otherReferences.map((reference) => /* @__PURE__ */ jsx(
73
+ DocumentPreview,
74
+ {
75
+ value: reference,
76
+ type: reference._type
77
+ },
78
+ reference._id
79
+ ))
80
+ ] }) : null
81
+ ] }) }),
82
+ otherReferences.length === 0 ? /* @__PURE__ */ jsx(Text, { children: "This document has no other references." }) : /* @__PURE__ */ jsx(Text, { children: "You may not be able to delete this document because other documents refer to it." })
83
+ ] });
84
+ }
85
+ function DeleteTranslationFooter(props) {
86
+ const { translations, onClose, onProceed } = props;
87
+ return /* @__PURE__ */ jsxs(Grid, { columns: 2, gap: 2, children: [
88
+ /* @__PURE__ */ jsx(Button, { text: "Cancel", onClick: onClose, mode: "ghost" }),
89
+ /* @__PURE__ */ jsx(
90
+ Button,
91
+ {
92
+ text: translations && translations.length > 0 ? "Unset translation reference" : "Delete document",
93
+ onClick: onProceed,
94
+ tone: "critical"
95
+ }
96
+ )
97
+ ] });
98
+ }
99
+ const isPromise = (promise) => typeof promise == "object" && typeof promise.then == "function", globalCache = [];
100
+ function shallowEqualArrays(arrA, arrB, equal = (a, b) => a === b) {
101
+ if (arrA === arrB)
102
+ return !0;
103
+ if (!arrA || !arrB)
104
+ return !1;
105
+ const len = arrA.length;
106
+ if (arrB.length !== len)
107
+ return !1;
108
+ for (let i = 0; i < len; i++)
109
+ if (!equal(arrA[i], arrB[i]))
110
+ return !1;
111
+ return !0;
112
+ }
113
+ function query$1(fn, keys = null, preload = !1, config = {}) {
114
+ keys === null && (keys = [fn]);
115
+ for (const entry2 of globalCache)
116
+ if (shallowEqualArrays(keys, entry2.keys, entry2.equal)) {
117
+ if (preload)
118
+ return;
119
+ if (Object.prototype.hasOwnProperty.call(entry2, "error"))
120
+ throw entry2.error;
121
+ if (Object.prototype.hasOwnProperty.call(entry2, "response"))
122
+ return config.lifespan && config.lifespan > 0 && (entry2.timeout && clearTimeout(entry2.timeout), entry2.timeout = setTimeout(entry2.remove, config.lifespan)), entry2.response;
123
+ if (!preload)
124
+ throw entry2.promise;
125
+ }
126
+ const entry = {
127
+ keys,
128
+ equal: config.equal,
129
+ remove: () => {
130
+ const index = globalCache.indexOf(entry);
131
+ index !== -1 && globalCache.splice(index, 1);
132
+ },
133
+ promise: (
134
+ // Execute the promise
135
+ (isPromise(fn) ? fn : fn(...keys)).then((response) => {
136
+ entry.response = response, config.lifespan && config.lifespan > 0 && (entry.timeout = setTimeout(entry.remove, config.lifespan));
137
+ }).catch((error) => entry.error = error)
138
+ )
139
+ };
140
+ if (globalCache.push(entry), !preload)
141
+ throw entry.promise;
142
+ }
143
+ const suspend = (fn, keys, config) => query$1(fn, keys, !1, config), DocumentInternationalizationContext = createContext(DEFAULT_CONFIG);
144
+ function useDocumentInternationalizationContext() {
145
+ return useContext(DocumentInternationalizationContext);
146
+ }
147
+ function DocumentInternationalizationProvider(props) {
148
+ const { pluginConfig } = props, client = useClient({ apiVersion: pluginConfig.apiVersion }), workspace = useWorkspace(), supportedLanguages = Array.isArray(pluginConfig.supportedLanguages) ? pluginConfig.supportedLanguages : (
149
+ // eslint-disable-next-line require-await
150
+ suspend(async () => typeof pluginConfig.supportedLanguages == "function" ? pluginConfig.supportedLanguages(client) : pluginConfig.supportedLanguages, [workspace])
151
+ );
152
+ return /* @__PURE__ */ jsx(
153
+ DocumentInternationalizationContext.Provider,
154
+ {
155
+ value: { ...pluginConfig, supportedLanguages },
156
+ children: props.renderDefault(props)
157
+ }
158
+ );
159
+ }
160
+ const DeleteTranslationAction = (props) => {
161
+ const { id: documentId, published, draft } = props, doc = draft || published, { languageField } = useDocumentInternationalizationContext(), [isDialogOpen, setDialogOpen] = useState(!1), [translations, setTranslations] = useState([]), onClose = useCallback(() => setDialogOpen(!1), []), documentLanguage = doc ? doc[languageField] : null, toast = useToast(), client = useClient({ apiVersion: API_VERSION }), onProceed = useCallback(() => {
162
+ const tx = client.transaction();
163
+ let operation = "DELETE";
164
+ documentLanguage && translations.length > 0 ? (operation = "UNSET", translations.forEach((translation) => {
165
+ tx.patch(
166
+ translation._id,
167
+ (patch) => patch.unset([
168
+ `${TRANSLATIONS_ARRAY_NAME}[_key == "${documentLanguage}"]`
169
+ ])
170
+ );
171
+ })) : (tx.delete(documentId), tx.delete(`drafts.${documentId}`)), tx.commit().then(() => {
172
+ operation === "DELETE" && onClose(), toast.push({
173
+ status: "success",
174
+ title: operation === "UNSET" ? "Translation reference unset" : "Document deleted",
175
+ description: operation === "UNSET" ? "The document can now be deleted" : null
176
+ });
177
+ }).catch((err) => {
178
+ toast.push({
179
+ status: "error",
180
+ title: operation === "unset" ? "Failed to unset translation reference" : "Failed to delete document",
181
+ description: err.message
182
+ });
183
+ });
184
+ }, [client, documentLanguage, translations, documentId, onClose, toast]);
185
+ return {
186
+ label: "Delete translation...",
187
+ disabled: !doc || !documentLanguage,
188
+ icon: TrashIcon,
189
+ tone: "critical",
190
+ onHandle: () => {
191
+ setDialogOpen(!0);
192
+ },
193
+ dialog: isDialogOpen && {
194
+ type: "dialog",
195
+ onClose,
196
+ header: "Delete translation",
197
+ content: doc ? /* @__PURE__ */ jsx(
198
+ DeleteTranslationDialog,
199
+ {
200
+ doc,
201
+ documentId,
202
+ setTranslations
203
+ }
204
+ ) : null,
205
+ footer: /* @__PURE__ */ jsx(
206
+ DeleteTranslationFooter,
207
+ {
208
+ onClose,
209
+ onProceed,
210
+ translations
211
+ }
212
+ )
213
+ }
214
+ };
215
+ }, query = `*[_type == $translationSchema && $id in translations[].value._ref]{
216
+ _id,
217
+ _createdAt,
218
+ translations
219
+ }`;
220
+ function useTranslationMetadata(id) {
221
+ const { data, loading, error } = useListeningQuery(query, {
222
+ params: { id, translationSchema: METADATA_SCHEMA_NAME }
223
+ });
224
+ return { data, loading, error };
225
+ }
226
+ function useOpenInNewPane(id, type) {
227
+ const routerContext = useContext(RouterContext), { routerPanesState, groupIndex } = usePaneRouter();
228
+ return useCallback(() => {
229
+ if (!routerContext || !id || !type)
230
+ return;
231
+ if (!routerPanesState.length) {
232
+ routerContext.navigateIntent("edit", { id, type });
233
+ return;
234
+ }
235
+ const panes = [...routerPanesState];
236
+ panes.splice(groupIndex + 1, 0, [
237
+ {
238
+ id,
239
+ params: { type }
240
+ }
241
+ ]);
242
+ const href = routerContext.resolvePathFromState({ panes });
243
+ routerContext.navigateUrl({ path: href });
244
+ }, [id, type, routerContext, routerPanesState, groupIndex]);
245
+ }
246
+ function LanguageManage(props) {
247
+ const { id } = props, open = useOpenInNewPane(id, METADATA_SCHEMA_NAME);
248
+ return /* @__PURE__ */ jsx(
249
+ Tooltip,
250
+ {
251
+ animate: !0,
252
+ content: id ? null : /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "Document has no other translations" }) }),
253
+ fallbackPlacements: ["right", "left"],
254
+ placement: "top",
255
+ portal: !0,
256
+ children: /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(
257
+ Button,
258
+ {
259
+ disabled: !id,
260
+ mode: "ghost",
261
+ text: "Manage Translations",
262
+ icon: CogIcon,
263
+ onClick: () => open()
264
+ }
265
+ ) })
266
+ }
267
+ );
268
+ }
269
+ function createReference(key, ref, type, strengthenOnPublish = !0) {
270
+ return {
271
+ _key: key,
272
+ _type: "internationalizedArrayReferenceValue",
273
+ value: {
274
+ _type: "reference",
275
+ _ref: ref,
276
+ _weak: !0,
277
+ // If the user has configured weakReferences, we won't want to strengthen them
278
+ ...strengthenOnPublish ? { _strengthenOnPublish: { type } } : {}
279
+ }
280
+ };
281
+ }
282
+ function removeExcludedPaths(doc, schemaType) {
283
+ if (!isDocumentSchemaType(schemaType) || !doc)
284
+ return doc;
285
+ const pathsToExclude = extractPaths(doc, schemaType, []).filter(
286
+ (field) => {
287
+ var _a, _b, _c;
288
+ return ((_c = (_b = (_a = field.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.documentInternationalization) == null ? void 0 : _c.exclude) === !0;
289
+ }
290
+ ).map((field) => pathToString(field.path));
291
+ return new Mutation({
292
+ mutations: [
293
+ {
294
+ patch: {
295
+ id: doc._id,
296
+ unset: pathsToExclude
297
+ }
298
+ }
299
+ ]
300
+ }).apply(doc);
301
+ }
302
+ function extractPaths(doc, schemaType, path) {
303
+ return schemaType.fields.reduce((acc, field) => {
304
+ var _a, _b;
305
+ const fieldPath = [...path, field.name], fieldSchema = field.type, { value } = (_a = extractWithPath(pathToString(fieldPath), doc)[0]) != null ? _a : {};
306
+ if (!value)
307
+ return acc;
308
+ const thisFieldWithPath = {
309
+ path: fieldPath,
310
+ name: field.name,
311
+ schemaType: fieldSchema,
312
+ value
313
+ };
314
+ if (fieldSchema.jsonType === "object") {
315
+ const innerFields = extractPaths(doc, fieldSchema, fieldPath);
316
+ return [...acc, thisFieldWithPath, ...innerFields];
317
+ } else if (fieldSchema.jsonType === "array" && fieldSchema.of.length && fieldSchema.of.some((item) => "fields" in item)) {
318
+ const { value: arrayValue } = (_b = extractWithPath(pathToString(fieldPath), doc)[0]) != null ? _b : {};
319
+ let arrayPaths = [];
320
+ if (arrayValue != null && arrayValue.length)
321
+ for (const item of arrayValue) {
322
+ const itemPath = [...fieldPath, { _key: item._key }];
323
+ let itemSchema = fieldSchema.of.find((t) => t.name === item._type);
324
+ if (item._type || (itemSchema = fieldSchema.of[0]), item._key && itemSchema) {
325
+ const innerFields = extractPaths(
326
+ doc,
327
+ itemSchema,
328
+ itemPath
329
+ ), arrayMember = {
330
+ path: itemPath,
331
+ name: item._key,
332
+ schemaType: itemSchema,
333
+ value: item
334
+ };
335
+ arrayPaths = [...arrayPaths, arrayMember, ...innerFields];
336
+ }
337
+ }
338
+ return [...acc, thisFieldWithPath, ...arrayPaths];
339
+ }
340
+ return [...acc, thisFieldWithPath];
341
+ }, []);
342
+ }
343
+ function LanguageOption(props) {
344
+ var _a;
345
+ const {
346
+ language,
347
+ schemaType,
348
+ documentId,
349
+ current,
350
+ source,
351
+ sourceLanguageId,
352
+ metadata: metadata2,
353
+ metadataId
354
+ } = props, [userHasClicked, setUserHasClicked] = useState(!1), disabled = props.disabled || userHasClicked || current || !source || !sourceLanguageId || !metadataId, translation = metadata2 != null && metadata2.translations.length ? metadata2.translations.find((t) => t._key === language.id) : void 0, { apiVersion, languageField, weakReferences } = useDocumentInternationalizationContext(), client = useClient({ apiVersion }), toast = useToast(), open = useOpenInNewPane((_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref, schemaType.name), handleOpen = useCallback(() => open(), [open]);
355
+ useEffect(() => {
356
+ setUserHasClicked(!1);
357
+ }, [!!translation]);
358
+ const handleCreate = useCallback(async () => {
359
+ if (!source)
360
+ throw new Error("Cannot create translation without source document");
361
+ if (!sourceLanguageId)
362
+ throw new Error("Cannot create translation without source language ID");
363
+ if (!metadataId)
364
+ throw new Error("Cannot create translation without a metadata ID");
365
+ setUserHasClicked(!0);
366
+ const transaction = client.transaction(), newTranslationDocumentId = uuid();
367
+ let newTranslationDocument = {
368
+ ...source,
369
+ _id: `drafts.${newTranslationDocumentId}`,
370
+ // 2. Update language of the translation
371
+ [languageField]: language.id
372
+ };
373
+ newTranslationDocument = removeExcludedPaths(
374
+ newTranslationDocument,
375
+ schemaType
376
+ ), transaction.create(newTranslationDocument);
377
+ const sourceReference = createReference(
378
+ sourceLanguageId,
379
+ documentId,
380
+ schemaType.name,
381
+ !weakReferences
382
+ ), newTranslationReference = createReference(
383
+ language.id,
384
+ newTranslationDocumentId,
385
+ schemaType.name,
386
+ !weakReferences
387
+ ), newMetadataDocument = {
388
+ _id: metadataId,
389
+ _type: METADATA_SCHEMA_NAME,
390
+ schemaTypes: [schemaType.name],
391
+ translations: [sourceReference]
392
+ };
393
+ transaction.createIfNotExists(newMetadataDocument);
394
+ const metadataPatch = client.patch(metadataId).setIfMissing({ translations: [sourceReference] }).insert("after", "translations[-1]", [newTranslationReference]);
395
+ transaction.patch(metadataPatch), transaction.commit().then(() => {
396
+ const metadataExisted = !!(metadata2 != null && metadata2._createdAt);
397
+ return toast.push({
398
+ status: "success",
399
+ title: `Created "${language.title}" translation`,
400
+ description: metadataExisted ? "Updated Translations Metadata" : "Created Translations Metadata"
401
+ });
402
+ }).catch((err) => (console.error(err), setUserHasClicked(!1), toast.push({
403
+ status: "error",
404
+ title: "Error creating translation",
405
+ description: err.message
406
+ })));
407
+ }, [
408
+ client,
409
+ documentId,
410
+ language.id,
411
+ language.title,
412
+ languageField,
413
+ metadata2 == null ? void 0 : metadata2._createdAt,
414
+ metadataId,
415
+ schemaType,
416
+ source,
417
+ sourceLanguageId,
418
+ toast,
419
+ weakReferences
420
+ ]);
421
+ let message;
422
+ return current ? message = "Current document" : translation ? message = `Open ${language.title} translation` : translation || (message = `Create new ${language.title} translation`), /* @__PURE__ */ jsx(
423
+ Tooltip,
424
+ {
425
+ animate: !0,
426
+ content: /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: message }) }),
427
+ fallbackPlacements: ["right", "left"],
428
+ placement: "top",
429
+ portal: !0,
430
+ children: /* @__PURE__ */ jsx(
431
+ Button,
432
+ {
433
+ onClick: translation ? handleOpen : handleCreate,
434
+ mode: current && disabled ? "default" : "bleed",
435
+ disabled,
436
+ children: /* @__PURE__ */ jsxs(Flex, { gap: 3, align: "center", children: [
437
+ disabled && !current ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsx(Text, { size: 2, children: translation ? /* @__PURE__ */ jsx(SplitVerticalIcon, {}) : current ? /* @__PURE__ */ jsx(CheckmarkIcon, {}) : /* @__PURE__ */ jsx(AddIcon, {}) }),
438
+ /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(Text, { children: language.title }) }),
439
+ /* @__PURE__ */ jsx(Badge, { tone: disabled || current ? "default" : "primary", children: language.id })
440
+ ] })
441
+ }
442
+ )
443
+ }
444
+ );
445
+ }
446
+ function LanguagePatch(props) {
447
+ const { language, source } = props, { apiVersion, languageField } = useDocumentInternationalizationContext(), disabled = props.disabled || !source, client = useClient({ apiVersion }), toast = useToast(), handleClick = useCallback(() => {
448
+ if (!source)
449
+ throw new Error("Cannot patch missing document");
450
+ const currentId = source._id;
451
+ client.patch(currentId).set({ [languageField]: language.id }).commit().then(() => {
452
+ toast.push({
453
+ title: `Set document language to ${language.title}`,
454
+ status: "success"
455
+ });
456
+ }).catch((err) => (console.error(err), toast.push({
457
+ title: `Failed to set document language to ${language.title}`,
458
+ status: "error"
459
+ })));
460
+ }, [source, client, languageField, language, toast]);
461
+ return /* @__PURE__ */ jsx(
462
+ Button,
463
+ {
464
+ mode: "bleed",
465
+ onClick: handleClick,
466
+ disabled,
467
+ justify: "flex-start",
468
+ children: /* @__PURE__ */ jsxs(Flex, { gap: 3, align: "center", children: [
469
+ /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(EditIcon, {}) }),
470
+ /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsx(Text, { children: language.title }) }),
471
+ /* @__PURE__ */ jsx(Badge, { children: language.id })
472
+ ] })
473
+ }
474
+ );
475
+ }
476
+ var ConstrainedBox = styled(Box)`
477
+ max-width: 280px;
478
+ `;
479
+ function Warning({ children }) {
480
+ return /* @__PURE__ */ jsx(Card, { tone: "caution", padding: 3, children: /* @__PURE__ */ jsx(Flex, { justify: "center", children: /* @__PURE__ */ jsx(ConstrainedBox, { children: /* @__PURE__ */ jsx(Text, { size: 1, align: "center", children }) }) }) });
481
+ }
482
+ function DocumentInternationalizationMenu(props) {
483
+ const { documentId } = props, schemaType = props.schemaType, { languageField, supportedLanguages } = useDocumentInternationalizationContext(), [query2, setQuery] = useState(""), handleQuery = useCallback((event) => {
484
+ event.currentTarget.value ? setQuery(event.currentTarget.value) : setQuery("");
485
+ }, []), [open, setOpen] = useState(!1), handleClick = useCallback(() => setOpen((o) => !o), []), [button, setButton] = useState(null), [popover, setPopover] = useState(null), handleClickOutside = useCallback(() => setOpen(!1), []);
486
+ useClickOutside(handleClickOutside, [button, popover]);
487
+ const { data, loading, error } = useTranslationMetadata(documentId), metadata2 = Array.isArray(data) && data.length ? data[0] : null, metadataId = useMemo(() => {
488
+ var _a;
489
+ return loading ? null : (_a = metadata2 == null ? void 0 : metadata2._id) != null ? _a : uuid();
490
+ }, [loading, metadata2 == null ? void 0 : metadata2._id]), { draft, published } = useEditState(documentId, schemaType.name), source = draft || published, documentIsInOneMetadataDocument = useMemo(() => Array.isArray(data) && data.length <= 1, [data]), sourceLanguageId = source == null ? void 0 : source[languageField], sourceLanguageIsValid = supportedLanguages.some(
491
+ (l) => l.id === sourceLanguageId
492
+ ), allLanguagesAreValid = useMemo(() => {
493
+ const valid = supportedLanguages.every((l) => l.id && l.title);
494
+ return valid || console.warn(
495
+ '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.',
496
+ supportedLanguages
497
+ ), valid;
498
+ }, [supportedLanguages]), content = /* @__PURE__ */ jsx(Box, { padding: 1, children: error ? /* @__PURE__ */ jsx(Card, { tone: "critical", padding: 1, children: /* @__PURE__ */ jsx(Text, { children: "There was an error returning translations metadata" }) }) : /* @__PURE__ */ jsxs(Stack, { space: 1, children: [
499
+ /* @__PURE__ */ jsx(LanguageManage, { id: metadata2 == null ? void 0 : metadata2._id }),
500
+ supportedLanguages.length > 4 ? /* @__PURE__ */ jsx(
501
+ TextInput,
502
+ {
503
+ onChange: handleQuery,
504
+ value: query2,
505
+ placeholder: "Filter languages"
506
+ }
507
+ ) : null,
508
+ supportedLanguages.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
509
+ loading ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
510
+ data && documentIsInOneMetadataDocument ? null : /* @__PURE__ */ jsx(Warning, { children: "This document has been found in more than one Translations Metadata documents" }),
511
+ allLanguagesAreValid ? null : /* @__PURE__ */ jsx(Warning, { children: "Not all language objects are valid. See the console." }),
512
+ sourceLanguageId ? null : /* @__PURE__ */ jsxs(Warning, { children: [
513
+ "Choose a language to apply to",
514
+ " ",
515
+ /* @__PURE__ */ jsx("strong", { children: "this Document" })
516
+ ] }),
517
+ sourceLanguageId && !sourceLanguageIsValid ? /* @__PURE__ */ jsxs(Warning, { children: [
518
+ "Select a supported language. Current language value:",
519
+ " ",
520
+ /* @__PURE__ */ jsx("code", { children: sourceLanguageId })
521
+ ] }) : null
522
+ ] }),
523
+ supportedLanguages.filter((language) => query2 ? language.title.toLowerCase().includes(query2.toLowerCase()) : !0).map(
524
+ (language) => {
525
+ var _a;
526
+ return !loading && sourceLanguageId && sourceLanguageIsValid ? (
527
+ // Button to duplicate this document to a new translation
528
+ // And either create or update the metadata document
529
+ /* @__PURE__ */ jsx(
530
+ LanguageOption,
531
+ {
532
+ language,
533
+ schemaType,
534
+ documentId,
535
+ disabled: loading || !allLanguagesAreValid,
536
+ current: language.id === sourceLanguageId,
537
+ metadata: metadata2,
538
+ metadataId,
539
+ source,
540
+ sourceLanguageId
541
+ },
542
+ language.id
543
+ )
544
+ ) : (
545
+ // Button to set a language field on *this* document
546
+ /* @__PURE__ */ jsx(
547
+ LanguagePatch,
548
+ {
549
+ source,
550
+ language,
551
+ disabled: (_a = loading || !allLanguagesAreValid || (metadata2 == null ? void 0 : metadata2.translations.filter((t) => {
552
+ var _a2;
553
+ return ((_a2 = t == null ? void 0 : t.value) == null ? void 0 : _a2._ref) !== documentId;
554
+ }).some((t) => t._key === language.id))) != null ? _a : !1
555
+ },
556
+ language.id
557
+ )
558
+ );
559
+ }
560
+ )
561
+ ] }) : null
562
+ ] }) }), issueWithTranslations = !loading && sourceLanguageId && !sourceLanguageIsValid;
563
+ return !documentId || !schemaType || !schemaType.name ? null : /* @__PURE__ */ jsx(
564
+ Popover,
565
+ {
566
+ animate: !0,
567
+ constrainSize: !0,
568
+ content,
569
+ open,
570
+ portal: !0,
571
+ ref: setPopover,
572
+ overflow: "auto",
573
+ tone: "default",
574
+ children: /* @__PURE__ */ jsx(
575
+ Button,
576
+ {
577
+ text: "Translations",
578
+ mode: "bleed",
579
+ disabled: !source,
580
+ tone: !source || loading || !issueWithTranslations ? void 0 : "caution",
581
+ icon: TranslateIcon,
582
+ onClick: handleClick,
583
+ ref: setButton,
584
+ selected: open
585
+ }
586
+ )
587
+ }
588
+ );
589
+ }
590
+ const DeleteMetadataAction = (props) => {
591
+ const { id: documentId, published, draft, onComplete } = props, doc = draft || published, [isDialogOpen, setDialogOpen] = useState(!1), onClose = useCallback(() => setDialogOpen(!1), []), translations = useMemo(
592
+ () => doc && Array.isArray(doc[TRANSLATIONS_ARRAY_NAME]) ? doc[TRANSLATIONS_ARRAY_NAME] : [],
593
+ [doc]
594
+ ), toast = useToast(), client = useClient({ apiVersion: API_VERSION }), onProceed = useCallback(() => {
595
+ const tx = client.transaction();
596
+ tx.patch(documentId, (patch) => patch.unset([TRANSLATIONS_ARRAY_NAME])), translations.length > 0 && translations.forEach((translation) => {
597
+ tx.delete(translation.value._ref), tx.delete(`drafts.${translation.value._ref}`);
598
+ }), tx.delete(documentId), tx.delete(`drafts.${documentId}`), tx.commit().then(() => {
599
+ onClose(), toast.push({
600
+ status: "success",
601
+ title: "Deleted document and translations"
602
+ });
603
+ }).catch((err) => {
604
+ toast.push({
605
+ status: "error",
606
+ title: "Failed to delete document and translations",
607
+ description: err.message
608
+ });
609
+ });
610
+ }, [client, translations, documentId, onClose, toast]);
611
+ return {
612
+ label: "Delete all translations",
613
+ disabled: !doc || !translations.length,
614
+ icon: TrashIcon,
615
+ tone: "critical",
616
+ onHandle: () => {
617
+ setDialogOpen(!0);
618
+ },
619
+ dialog: isDialogOpen && {
620
+ type: "confirm",
621
+ onCancel: onComplete,
622
+ onConfirm: () => {
623
+ onProceed(), onComplete();
624
+ },
625
+ tone: "critical",
626
+ message: translations.length === 1 ? "Delete 1 translation and this document" : `Delete all ${translations.length} translations and this document`
627
+ }
628
+ };
629
+ };
630
+ function LanguageBadge(props) {
631
+ var _a, _b;
632
+ const source = (props == null ? void 0 : props.draft) || (props == null ? void 0 : props.published), { languageField, supportedLanguages } = useDocumentInternationalizationContext(), languageId = source == null ? void 0 : source[languageField];
633
+ if (!languageId)
634
+ return null;
635
+ const language = Array.isArray(supportedLanguages) ? supportedLanguages.find((l) => l.id === languageId) : null;
636
+ return {
637
+ label: (_a = language == null ? void 0 : language.id) != null ? _a : String(languageId),
638
+ title: (_b = language == null ? void 0 : language.title) != null ? _b : void 0,
639
+ color: "primary"
640
+ };
641
+ }
642
+ function DocumentCheck(props) {
643
+ const {
644
+ id,
645
+ onCheckComplete,
646
+ addInvalidId,
647
+ removeInvalidId,
648
+ addDraftId,
649
+ removeDraftId
650
+ } = props, editState = useEditState(id, ""), { isValidating, validation } = useValidationStatus(id, ""), schema = useSchema(), validationHasErrors = useMemo(() => !isValidating && validation.length > 0 && validation.some((item) => item.level === "error"), [isValidating, validation]);
651
+ if (useEffect(() => {
652
+ validationHasErrors ? addInvalidId(id) : removeInvalidId(id), editState.draft ? addDraftId(id) : removeDraftId(id), isValidating || onCheckComplete(id);
653
+ }, [
654
+ addDraftId,
655
+ addInvalidId,
656
+ editState.draft,
657
+ id,
658
+ isValidating,
659
+ onCheckComplete,
660
+ removeDraftId,
661
+ removeInvalidId,
662
+ validationHasErrors
663
+ ]), !editState.draft)
664
+ return null;
665
+ const schemaType = schema.get(editState.draft._type);
666
+ return /* @__PURE__ */ jsx(
667
+ Card,
668
+ {
669
+ border: !0,
670
+ padding: 2,
671
+ tone: validationHasErrors ? "critical" : "positive",
672
+ children: editState.draft && schemaType ? /* @__PURE__ */ jsx(
673
+ Preview,
674
+ {
675
+ layout: "default",
676
+ value: editState.draft,
677
+ schemaType
678
+ }
679
+ ) : /* @__PURE__ */ jsx(Spinner, {})
680
+ }
681
+ );
682
+ }
683
+ function InfoIcon(props) {
684
+ const { text, icon, tone, children } = props;
685
+ return /* @__PURE__ */ jsx(
686
+ Tooltip,
687
+ {
688
+ animate: !0,
689
+ portal: !0,
690
+ content: children ? /* @__PURE__ */ jsx(Fragment, { children }) : /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsx(Text, { size: 1, children: text }) }),
691
+ children: /* @__PURE__ */ jsx(TextWithTone, { tone, size: 1, children: /* @__PURE__ */ jsx(icon, {}) })
692
+ }
693
+ );
694
+ }
695
+ function Info() {
696
+ return /* @__PURE__ */ jsx(InfoIcon, { icon: InfoOutlineIcon, tone: "primary", children: /* @__PURE__ */ jsxs(Stack, { padding: 3, space: 4, style: { maxWidth: 250 }, children: [
697
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: 1, children: "Bulk publishing uses the Scheduling API." }) }),
698
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: 1, children: "Customized Document Actions in the Studio will not execute. Webhooks will execute." }) }),
699
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: 1, children: "Validation is checked before rendering the button below, but the Scheduling API will not check for \u2013 or enforce \u2013 validation." }) })
700
+ ] }) });
701
+ }
702
+ function BulkPublish(props) {
703
+ const { translations } = props, client = useClient({ apiVersion: API_VERSION }), { projectId, dataset } = useWorkspace(), toast = useToast(), [invalidIds, setInvalidIds] = useState(null), [checkedIds, setCheckedIds] = useState([]), onCheckComplete = useCallback((id) => {
704
+ setCheckedIds((ids) => Array.from(/* @__PURE__ */ new Set([...ids, id])));
705
+ }, []), [open, setOpen] = useState(!1), onOpen = useCallback(() => setOpen(!0), []), onClose = useCallback(() => setOpen(!1), []), addInvalidId = useCallback((id) => {
706
+ setInvalidIds((ids) => ids ? Array.from(/* @__PURE__ */ new Set([...ids, id])) : [id]);
707
+ }, []), removeInvalidId = useCallback((id) => {
708
+ setInvalidIds((ids) => ids ? ids.filter((i) => i !== id) : []);
709
+ }, []), [draftIds, setDraftIds] = useState([]), addDraftId = useCallback((id) => {
710
+ setDraftIds((ids) => Array.from(/* @__PURE__ */ new Set([...ids, id])));
711
+ }, []), removeDraftId = useCallback((id) => {
712
+ setDraftIds((ids) => ids.filter((i) => i !== id));
713
+ }, []), handleBulkPublish = useCallback(() => {
714
+ const body = translations.map((translation) => ({
715
+ documentId: translation.value._ref
716
+ }));
717
+ client.request({
718
+ uri: `/publish/${projectId}/${dataset}`,
719
+ method: "POST",
720
+ body
721
+ }).then(() => {
722
+ toast.push({
723
+ status: "success",
724
+ title: "Success",
725
+ description: "Bulk publish complete"
726
+ });
727
+ }).catch((err) => {
728
+ console.error(err), toast.push({
729
+ status: "error",
730
+ title: "Error",
731
+ description: "Bulk publish failed"
732
+ });
733
+ });
734
+ }, [translations, client, projectId, dataset, toast]), disabled = (
735
+ // Not all documents have been checked
736
+ checkedIds.length !== translations.length || // Some document(s) are invalid
737
+ !!(invalidIds && (invalidIds == null ? void 0 : invalidIds.length) > 0) || // No documents are drafts
738
+ !draftIds.length
739
+ );
740
+ return (translations == null ? void 0 : translations.length) > 0 ? /* @__PURE__ */ jsx(Card, { padding: 4, border: !0, radius: 2, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
741
+ /* @__PURE__ */ jsxs(Inline, { space: 3, children: [
742
+ /* @__PURE__ */ jsx(Text, { weight: "bold", size: 1, children: "Bulk publishing" }),
743
+ /* @__PURE__ */ jsx(Info, {})
744
+ ] }),
745
+ /* @__PURE__ */ jsx(Stack, { children: /* @__PURE__ */ jsx(
746
+ Button,
747
+ {
748
+ onClick: onOpen,
749
+ text: "Prepare bulk publishing",
750
+ mode: "ghost"
751
+ }
752
+ ) }),
753
+ open && /* @__PURE__ */ jsx(
754
+ Dialog,
755
+ {
756
+ animate: !0,
757
+ header: "Bulk publishing",
758
+ id: "bulk-publish-dialog",
759
+ onClose,
760
+ zOffset: 1e3,
761
+ width: 3,
762
+ children: /* @__PURE__ */ jsxs(Stack, { space: 4, padding: 4, children: [
763
+ draftIds.length > 0 ? /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
764
+ /* @__PURE__ */ jsxs(Text, { size: 1, children: [
765
+ "There",
766
+ " ",
767
+ draftIds.length === 1 ? "is 1 draft document" : `are ${draftIds.length} draft documents`,
768
+ "."
769
+ ] }),
770
+ invalidIds && invalidIds.length > 0 ? /* @__PURE__ */ jsxs(TextWithTone, { tone: "critical", size: 1, children: [
771
+ invalidIds && invalidIds.length === 1 ? "1 draft document has" : `${invalidIds && invalidIds.length} draft documents have`,
772
+ " ",
773
+ "validation issues that must addressed first"
774
+ ] }) : /* @__PURE__ */ jsx(TextWithTone, { tone: "positive", size: 1, children: "All drafts are valid and can be bulk published" })
775
+ ] }) : null,
776
+ /* @__PURE__ */ jsx(Stack, { space: 1, children: translations.filter((translation) => {
777
+ var _a;
778
+ return (_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref;
779
+ }).map((translation) => /* @__PURE__ */ jsx(
780
+ DocumentCheck,
781
+ {
782
+ id: translation.value._ref,
783
+ onCheckComplete,
784
+ addInvalidId,
785
+ removeInvalidId,
786
+ addDraftId,
787
+ removeDraftId
788
+ },
789
+ translation._key
790
+ )) }),
791
+ draftIds.length > 0 ? /* @__PURE__ */ jsx(
792
+ Button,
793
+ {
794
+ mode: "ghost",
795
+ tone: invalidIds && (invalidIds == null ? void 0 : invalidIds.length) > 0 ? "caution" : "positive",
796
+ text: draftIds.length === 1 ? "Publish draft document" : `Bulk publish ${draftIds.length} draft documents`,
797
+ onClick: handleBulkPublish,
798
+ disabled
799
+ }
800
+ ) : /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: "No draft documents to publish" })
801
+ ] })
802
+ }
803
+ )
804
+ ] }) }) : null;
805
+ }
806
+ function ReferencePatcher(props) {
807
+ const { translation, documentType, metadataId } = props, editState = useEditState(translation.value._ref, documentType), client = useClient({ apiVersion: API_VERSION }), { onChange } = useDocumentPane();
808
+ return useEffect(() => {
809
+ if (
810
+ // We have a reference
811
+ translation.value._ref && // It's still weak and not-yet-strengthened
812
+ translation.value._weak && // We also want to keep this check because maybe the user *configured* weak refs
813
+ translation.value._strengthenOnPublish && // The referenced document has just been published
814
+ !editState.draft && editState.published && editState.ready
815
+ ) {
816
+ const referencePathBase = [
817
+ "translations",
818
+ { _key: translation._key },
819
+ "value"
820
+ ];
821
+ onChange(
822
+ new PatchEvent([
823
+ unset([...referencePathBase, "_weak"]),
824
+ unset([...referencePathBase, "_strengthenOnPublish"])
825
+ ])
826
+ );
827
+ }
828
+ }, [translation, editState, metadataId, client, onChange]), null;
829
+ }
830
+ function OptimisticallyStrengthen(props) {
831
+ const { translations = [], metadataId } = props;
832
+ return translations.length ? /* @__PURE__ */ jsx(Fragment, { children: translations.map(
833
+ (translation) => {
834
+ var _a;
835
+ return (_a = translation.value._strengthenOnPublish) != null && _a.type ? /* @__PURE__ */ jsx(
836
+ ReferencePatcher,
837
+ {
838
+ translation,
839
+ documentType: translation.value._strengthenOnPublish.type,
840
+ metadataId
841
+ },
842
+ translation._key
843
+ ) : null;
844
+ }
845
+ ) }) : null;
846
+ }
847
+ var metadata = (schemaTypes, metadataFields) => defineType({
848
+ type: "document",
849
+ name: METADATA_SCHEMA_NAME,
850
+ title: "Translation metadata",
851
+ icon: TranslateIcon,
852
+ liveEdit: !0,
853
+ fields: [
854
+ defineField({
855
+ name: TRANSLATIONS_ARRAY_NAME,
856
+ type: "internationalizedArrayReference"
857
+ }),
858
+ defineField({
859
+ name: "schemaTypes",
860
+ description: "Optional: Used to filter the reference fields above so all translations share the same types.",
861
+ type: "array",
862
+ of: [{ type: "string" }],
863
+ options: { list: schemaTypes },
864
+ readOnly: ({ value }) => !!value
865
+ }),
866
+ ...metadataFields
867
+ ],
868
+ preview: {
869
+ select: {
870
+ translations: TRANSLATIONS_ARRAY_NAME,
871
+ documentSchemaTypes: "schemaTypes"
872
+ },
873
+ prepare(selection) {
874
+ const { translations = [], documentSchemaTypes = [] } = selection, title = translations.length === 1 ? "1 Translation" : `${translations.length} Translations`, languageKeys = translations.length ? translations.map((t) => t._key.toUpperCase()).join(", ") : "", subtitle = [
875
+ languageKeys ? `(${languageKeys})` : null,
876
+ documentSchemaTypes != null && documentSchemaTypes.length ? documentSchemaTypes.map((s) => s).join(", ") : ""
877
+ ].filter(Boolean).join(" ");
878
+ return {
879
+ title,
880
+ subtitle
881
+ };
882
+ }
883
+ }
884
+ });
885
+ const documentInternationalization = definePlugin(
886
+ (config) => {
887
+ const pluginConfig = { ...DEFAULT_CONFIG, ...config }, {
888
+ supportedLanguages,
889
+ schemaTypes,
890
+ languageField,
891
+ bulkPublish,
892
+ metadataFields
893
+ } = pluginConfig;
894
+ if (schemaTypes.length === 0)
895
+ throw new Error(
896
+ "You must specify at least one schema type on which to enable document internationalization. Update the `schemaTypes` option in the documentInternationalization() configuration."
897
+ );
898
+ return {
899
+ name: "@sanity/document-internationalization",
900
+ studio: {
901
+ components: {
902
+ layout: (props) => DocumentInternationalizationProvider({ ...props, pluginConfig })
903
+ }
904
+ },
905
+ // Adds:
906
+ // - A bulk-publishing UI component to the form
907
+ // - Will only work for projects on a compatible plan
908
+ form: {
909
+ components: {
910
+ input: (props) => {
911
+ var _a, _b, _c;
912
+ if (props.id === "root" && props.schemaType.name === METADATA_SCHEMA_NAME && isSanityDocument(props == null ? void 0 : props.value)) {
913
+ const metadataId = (_a = props == null ? void 0 : props.value) == null ? void 0 : _a._id, translations = (_c = (_b = props == null ? void 0 : props.value) == null ? void 0 : _b.translations) != null ? _c : [], weakAndTypedTranslations = translations.filter(
914
+ ({ value }) => (value == null ? void 0 : value._weak) && value._strengthenOnPublish
915
+ );
916
+ return /* @__PURE__ */ jsxs(Stack, { space: 5, children: [
917
+ bulkPublish ? /* @__PURE__ */ jsx(BulkPublish, { translations }) : null,
918
+ weakAndTypedTranslations.length > 0 ? /* @__PURE__ */ jsx(
919
+ OptimisticallyStrengthen,
920
+ {
921
+ metadataId,
922
+ translations: weakAndTypedTranslations
923
+ }
924
+ ) : null,
925
+ props.renderDefault(props)
926
+ ] });
927
+ }
928
+ return props.renderDefault(props);
929
+ }
930
+ }
931
+ },
932
+ // Adds:
933
+ // - The `Translations` dropdown to the editing form
934
+ // - `Badges` to documents with a language value
935
+ // - The `DeleteMetadataAction` action to the metadata document type
936
+ document: {
937
+ unstable_languageFilter: (prev, ctx) => {
938
+ const { schemaType, documentId } = ctx;
939
+ return schemaTypes.includes(schemaType) && documentId ? [
940
+ ...prev,
941
+ (props) => DocumentInternationalizationMenu({ ...props, documentId })
942
+ ] : prev;
943
+ },
944
+ badges: (prev, { schemaType }) => schemaTypes.includes(schemaType) ? [(props) => LanguageBadge(props), ...prev] : prev,
945
+ actions: (prev, { schemaType }) => schemaType === METADATA_SCHEMA_NAME ? [...prev, DeleteMetadataAction] : prev
946
+ },
947
+ // Adds:
948
+ // - The `Translations metadata` document type to the schema
949
+ schema: {
950
+ // Create the metadata document type
951
+ types: [metadata(schemaTypes, metadataFields)],
952
+ // For every schema type this plugin is enabled on
953
+ // Create an initial value template to set the language
954
+ templates: (prev, { schema }) => {
955
+ if (!Array.isArray(supportedLanguages))
956
+ return prev;
957
+ const parameterizedTemplates = schemaTypes.map((schemaType) => {
958
+ var _a, _b;
959
+ return {
960
+ id: `${schemaType}-parameterized`,
961
+ title: `${(_b = (_a = schema == null ? void 0 : schema.get(schemaType)) == null ? void 0 : _a.title) != null ? _b : schemaType}: with Language`,
962
+ schemaType,
963
+ parameters: [
964
+ { name: "languageId", title: "Language ID", type: "string" }
965
+ ],
966
+ value: ({ languageId }) => ({
967
+ [languageField]: languageId
968
+ })
969
+ };
970
+ }), staticTemplates = schemaTypes.flatMap((schemaType) => supportedLanguages.map((language) => {
971
+ var _a, _b;
972
+ return {
973
+ id: `${schemaType}-${language.id}`,
974
+ title: `${language.title} ${(_b = (_a = schema == null ? void 0 : schema.get(schemaType)) == null ? void 0 : _a.title) != null ? _b : schemaType}`,
975
+ schemaType,
976
+ value: {
977
+ [languageField]: language.id
978
+ }
979
+ };
980
+ }));
981
+ return [...prev, ...parameterizedTemplates, ...staticTemplates];
982
+ }
983
+ },
984
+ // Uses:
985
+ // - `sanity-plugin-internationalized-array` to maintain the translations array
986
+ plugins: [
987
+ // Translation metadata stores its references using this plugin
988
+ // It cuts down on attribute usage and gives UI conveniences to add new translations
989
+ internationalizedArray({
990
+ languages: supportedLanguages,
991
+ fieldTypes: [
992
+ defineField(
993
+ {
994
+ name: "reference",
995
+ type: "reference",
996
+ to: schemaTypes.map((type) => ({ type })),
997
+ weak: pluginConfig.weakReferences,
998
+ // Reference filters don't actually enforce validation!
999
+ validation: (Rule) => (
1000
+ // @ts-expect-error - fix typings
1001
+ Rule.custom(async (item, context) => {
1002
+ var _a;
1003
+ if (!((_a = item == null ? void 0 : item.value) != null && _a._ref) || !(item != null && item._key))
1004
+ return !0;
1005
+ const valueLanguage = await context.getClient({ apiVersion: API_VERSION }).fetch(
1006
+ `*[_id in [$ref, $draftRef]][0].${languageField}`,
1007
+ {
1008
+ ref: item.value._ref,
1009
+ draftRef: `drafts.${item.value._ref}`
1010
+ }
1011
+ );
1012
+ return valueLanguage && valueLanguage === item._key ? !0 : "Referenced document does not have the correct language value";
1013
+ })
1014
+ ),
1015
+ options: {
1016
+ // @ts-expect-error - Update type once it knows the values of this filter
1017
+ filter: ({ parent, document }) => {
1018
+ if (!parent)
1019
+ return null;
1020
+ const language = (Array.isArray(parent) ? parent : [parent]).find((p) => p._key);
1021
+ return language != null && language._key ? document.schemaTypes ? {
1022
+ filter: `_type in $schemaTypes && ${languageField} == $language`,
1023
+ params: {
1024
+ schemaTypes: document.schemaTypes,
1025
+ language: language._key
1026
+ }
1027
+ } : {
1028
+ filter: `${languageField} == $language`,
1029
+ params: { language: language._key }
1030
+ } : null;
1031
+ }
1032
+ }
1033
+ },
1034
+ { strict: !1 }
1035
+ )
1036
+ ]
1037
+ })
1038
+ ]
1039
+ };
1040
+ }
1041
+ );
1042
+ export {
1043
+ DeleteTranslationAction,
1044
+ DocumentInternationalizationMenu,
1045
+ documentInternationalization,
1046
+ useDocumentInternationalizationContext
1047
+ };
1048
+ //# sourceMappingURL=index.mjs.map