@sanity/document-internationalization 2.0.0-studio-v3-plugin-v2.7 → 2.0.0-studio-v3-plugin-v2.8

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/lib/index.esm.js CHANGED
@@ -1,63 +1,172 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { defineType, defineField, useClient, useEditState, definePlugin } from 'sanity';
2
+ import { Card, Spinner, useToast, Stack, Text, Button, Tooltip, Box, Flex, Badge, useClickOutside, Popover } from '@sanity/ui';
3
+ import { useEditState, useValidationStatus, useSchema, Preview, useClient, useWorkspace, TextWithTone, PatchEvent, unset, defineType, defineField, definePlugin, isSanityDocument } from 'sanity';
3
4
  import { internationalizedArray } from 'sanity-plugin-internationalized-array';
4
- import { useToast, Button, Flex, Spinner, Text, Box, Badge, useClickOutside, Card, Stack, Popover } from '@sanity/ui';
5
- import { TranslateIcon, SplitVerticalIcon, CheckmarkIcon, AddIcon, CogIcon, ChevronRightIcon } from '@sanity/icons';
6
5
  import React, { useCallback, useState } from 'react';
7
- import { uuid } from '@sanity/uuid';
8
- import { usePaneRouter } from 'sanity/desk';
9
- import { RouterContext } from 'sanity/router';
6
+ import { CogIcon, SplitVerticalIcon, CheckmarkIcon, AddIcon, EditIcon, TranslateIcon } from '@sanity/icons';
10
7
  import { useListeningQuery } from 'sanity-plugin-utils';
8
+ import { usePaneRouter, useDocumentPane } from 'sanity/desk';
9
+ import { RouterContext } from 'sanity/router';
10
+ import { uuid } from '@sanity/uuid';
11
+ function LanguageBadge(props, supportedLanguages, languageField) {
12
+ var _a, _b;
13
+ const source = (props == null ? void 0 : props.draft) || (props == null ? void 0 : props.published);
14
+ const languageId = source == null ? void 0 : source[languageField];
15
+ if (!languageId) {
16
+ return null;
17
+ }
18
+ const language = Array.isArray(supportedLanguages) ? supportedLanguages.find(l => l.id === languageId) : null;
19
+ return {
20
+ label: (_a = language == null ? void 0 : language.id) != null ? _a : String(languageId),
21
+ title: (_b = language == null ? void 0 : language.title) != null ? _b : void 0,
22
+ color: "primary"
23
+ };
24
+ }
11
25
  const METADATA_SCHEMA_NAME = "translation.metadata";
12
26
  const API_VERSION = "2022-11-27";
13
- var metadata = schemaTypes => defineType({
14
- type: "document",
15
- name: METADATA_SCHEMA_NAME,
16
- title: "Translation metadata",
17
- icon: TranslateIcon,
18
- liveEdit: true,
19
- fields: [defineField({
20
- name: "translations",
21
- type: "internationalizedArrayReference"
22
- }), defineField({
23
- name: "schemaTypes",
24
- description: "Used to filter the reference fields above so all translations share the same types.",
25
- type: "array",
26
- // For some reason TS dislikes this line because of the DocumentDefinition return type
27
- // @ts-expect-error
28
- of: [{
29
- type: "string"
30
- }],
31
- options: {
32
- list: schemaTypes
33
- },
34
- readOnly: _ref => {
35
- let {
36
- value
37
- } = _ref;
38
- return Boolean(value);
27
+ function DocumentCheck(props) {
28
+ const {
29
+ id,
30
+ addInvalidId,
31
+ removeInvalidId,
32
+ addDraftId,
33
+ removeDraftId
34
+ } = props;
35
+ const editState = useEditState(id, "");
36
+ const {
37
+ isValidating,
38
+ validation
39
+ } = useValidationStatus(id, "");
40
+ const schema = useSchema();
41
+ const validationHasErrors = React.useMemo(() => {
42
+ return !isValidating && validation.length > 0 && validation.some(item => item.level === "error");
43
+ }, [isValidating, validation]);
44
+ React.useEffect(() => {
45
+ if (validationHasErrors) {
46
+ addInvalidId(id);
47
+ } else {
48
+ removeInvalidId(id);
39
49
  }
40
- })],
41
- preview: {
42
- select: {
43
- translations: "translations",
44
- documentSchemaTypes: "schemaTypes"
45
- },
46
- prepare(selection) {
47
- const {
48
- translations,
49
- documentSchemaTypes
50
- } = selection;
51
- const title = translations.length === 1 ? "1 Translation" : "".concat(translations.length, " Translations");
52
- const languageKeys = translations.length ? translations.map(t => t._key.toUpperCase()).join(", ") : "";
53
- const subtitle = [languageKeys ? "(".concat(languageKeys, ")") : null, (documentSchemaTypes == null ? void 0 : documentSchemaTypes.length) ? documentSchemaTypes.map(s => s.toUpperCase()).join(", ") : "No Schemas Defined"].filter(Boolean).join(" ");
54
- return {
55
- title,
56
- subtitle
57
- };
50
+ if (editState.draft) {
51
+ addDraftId(id);
52
+ } else {
53
+ removeDraftId(id);
58
54
  }
55
+ }, [addDraftId, addInvalidId, editState.draft, id, removeDraftId, removeInvalidId, validationHasErrors]);
56
+ if (!editState.draft) {
57
+ return null;
59
58
  }
60
- });
59
+ const schemaType = schema.get(editState.draft._type);
60
+ return /* @__PURE__ */jsx(Card, {
61
+ border: true,
62
+ padding: 2,
63
+ tone: validationHasErrors ? "critical" : "positive",
64
+ children: editState.draft && schemaType ? /* @__PURE__ */jsx(Preview, {
65
+ layout: "default",
66
+ value: editState.draft,
67
+ schemaType
68
+ }) : /* @__PURE__ */jsx(Spinner, {})
69
+ });
70
+ }
71
+ function BulkPublish(props) {
72
+ const {
73
+ translations
74
+ } = props;
75
+ const client = useClient({
76
+ apiVersion: API_VERSION
77
+ });
78
+ const {
79
+ projectId,
80
+ dataset
81
+ } = useWorkspace();
82
+ const toast = useToast();
83
+ const [invalidIds, setInvalidIds] = React.useState(null);
84
+ const addInvalidId = useCallback(id => {
85
+ setInvalidIds(ids => ids ? Array.from( /* @__PURE__ */new Set([...ids, id])) : [id]);
86
+ }, []);
87
+ const removeInvalidId = useCallback(id => {
88
+ setInvalidIds(ids => ids ? ids.filter(i => i !== id) : []);
89
+ }, []);
90
+ const [draftIds, setDraftIds] = React.useState([]);
91
+ const addDraftId = useCallback(id => {
92
+ setDraftIds(ids => Array.from( /* @__PURE__ */new Set([...ids, id])));
93
+ }, []);
94
+ const removeDraftId = useCallback(id => {
95
+ setDraftIds(ids => ids.filter(i => i !== id));
96
+ }, []);
97
+ const handleBulkPublish = useCallback(() => {
98
+ const body = translations.map(translation => ({
99
+ documentId: translation.value._ref
100
+ }));
101
+ client.request({
102
+ uri: "/publish/".concat(projectId, "/").concat(dataset),
103
+ method: "POST",
104
+ body
105
+ }).then(res => {
106
+ toast.push({
107
+ status: "success",
108
+ title: "Success",
109
+ description: "Bulk publish complete"
110
+ });
111
+ }).catch(err => {
112
+ console.error(err);
113
+ toast.push({
114
+ status: "error",
115
+ title: "Error",
116
+ description: "Bulk publish failed"
117
+ });
118
+ });
119
+ }, [translations, client, projectId, dataset, toast]);
120
+ return (translations == null ? void 0 : translations.length) > 0 ? /* @__PURE__ */jsx(Card, {
121
+ padding: 4,
122
+ border: true,
123
+ radius: 2,
124
+ children: /* @__PURE__ */jsxs(Stack, {
125
+ space: 3,
126
+ children: [/* @__PURE__ */jsx(Text, {
127
+ weight: "bold",
128
+ size: 1,
129
+ children: "Bulk publishing"
130
+ }), draftIds.length > 0 ? /* @__PURE__ */jsxs(Stack, {
131
+ space: 2,
132
+ children: [/* @__PURE__ */jsxs(Text, {
133
+ size: 1,
134
+ children: ["There", " ", draftIds.length === 1 ? "is 1 draft document" : "are ".concat(draftIds.length, " draft documents"), "."]
135
+ }), invalidIds && invalidIds.length > 0 ? /* @__PURE__ */jsxs(TextWithTone, {
136
+ tone: "caution",
137
+ size: 1,
138
+ children: [invalidIds && invalidIds.length === 1 ? "1 draft document has" : "".concat(invalidIds && invalidIds.length, " draft documents have"), " ", "validation issues that must addressed first"]
139
+ }) : /* @__PURE__ */jsx(TextWithTone, {
140
+ tone: "positive",
141
+ size: 1,
142
+ children: "All drafts are valid and can be bulk published"
143
+ })]
144
+ }) : null, /* @__PURE__ */jsx(Stack, {
145
+ space: 1,
146
+ children: translations.filter(translation => {
147
+ var _a;
148
+ return (_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref;
149
+ }).map(translation => /* @__PURE__ */jsx(DocumentCheck, {
150
+ id: translation.value._ref,
151
+ addInvalidId,
152
+ removeInvalidId,
153
+ addDraftId,
154
+ removeDraftId
155
+ }, translation._key))
156
+ }), draftIds.length > 0 ? /* @__PURE__ */jsx(Button, {
157
+ mode: "ghost",
158
+ tone: invalidIds && (invalidIds == null ? void 0 : invalidIds.length) > 0 ? "caution" : "positive",
159
+ text: draftIds.length === 1 ? "Publish draft document" : "Bulk publish ".concat(draftIds.length, " draft documents"),
160
+ onClick: handleBulkPublish,
161
+ disabled: Boolean(invalidIds && (invalidIds == null ? void 0 : invalidIds.length) > 0) || !draftIds.length
162
+ }) : /* @__PURE__ */jsx(Text, {
163
+ muted: true,
164
+ size: 1,
165
+ children: "No draft documents to publish"
166
+ })]
167
+ })
168
+ }) : null;
169
+ }
61
170
  function shallowEqualArrays(arrA, arrB) {
62
171
  let equal = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : (a, b) => a === b;
63
172
  if (arrA === arrB) return true;
@@ -109,6 +218,24 @@ function query(fn, keys) {
109
218
  return undefined;
110
219
  }
111
220
  const suspend = (fn, keys, config) => query(fn, keys, false, config);
221
+ function useTranslationMetadata(id, schemaType) {
222
+ const query = "*[_type == $translationSchema && $id in translations[].value._ref][0]";
223
+ const {
224
+ data,
225
+ loading,
226
+ error
227
+ } = useListeningQuery(query, {
228
+ params: {
229
+ id,
230
+ translationSchema: METADATA_SCHEMA_NAME
231
+ }
232
+ });
233
+ return {
234
+ data,
235
+ loading,
236
+ error
237
+ };
238
+ }
112
239
  function useOpenInNewPane(id, type) {
113
240
  const routerContext = React.useContext(RouterContext);
114
241
  const {
@@ -135,9 +262,23 @@ function useOpenInNewPane(id, type) {
135
262
  }, [id, type, routerContext, routerPanesState, groupIndex]);
136
263
  return openInNewPane;
137
264
  }
265
+ function LanguageManage(props) {
266
+ const {
267
+ id
268
+ } = props;
269
+ const open = useOpenInNewPane(id, METADATA_SCHEMA_NAME);
270
+ return /* @__PURE__ */jsx(Button, {
271
+ disabled: !id,
272
+ mode: "ghost",
273
+ text: "Manage Translations",
274
+ icon: CogIcon,
275
+ onClick: () => open()
276
+ });
277
+ }
138
278
  function createReference(key, ref, type) {
139
279
  return {
140
280
  _key: key,
281
+ _type: "internationalizedArrayReferenceValue",
141
282
  value: {
142
283
  _type: "reference",
143
284
  _ref: ref,
@@ -216,59 +357,49 @@ function LanguageOption(props) {
216
357
  });
217
358
  });
218
359
  }, [client, documentId, index, language, languageField, metadata == null ? void 0 : metadata._id, schemaType, sourceId, sourceLanguageId, toast]);
219
- return /* @__PURE__ */jsx(Button, {
220
- onClick: translation ? handleOpen : handleCreate,
221
- mode: current ? "default" : "bleed",
222
- disabled: disabled || current,
223
- children: /* @__PURE__ */jsxs(Flex, {
224
- gap: 3,
225
- align: "center",
226
- children: [disabled ? /* @__PURE__ */jsx(Spinner, {}) : /* @__PURE__ */jsx(Text, {
227
- size: 2,
228
- children: translation ? /* @__PURE__ */jsx(SplitVerticalIcon, {}) : current ? /* @__PURE__ */jsx(CheckmarkIcon, {}) : /* @__PURE__ */jsx(AddIcon, {})
229
- }), /* @__PURE__ */jsx(Box, {
230
- flex: 1,
231
- children: /* @__PURE__ */jsx(Text, {
232
- children: language.title
233
- })
234
- }), /* @__PURE__ */jsx(Badge, {
235
- tone: disabled || current ? "default" : "primary",
236
- children: language.id
237
- })]
360
+ let message;
361
+ if (current) {
362
+ message = "Current document";
363
+ } else if (translation) {
364
+ message = "Open ".concat(language.title, " translation");
365
+ } else if (!translation) {
366
+ message = "Create new ".concat(language.title, " translation");
367
+ }
368
+ return /* @__PURE__ */jsx(Tooltip, {
369
+ content: /* @__PURE__ */jsx(Box, {
370
+ padding: 2,
371
+ children: /* @__PURE__ */jsx(Text, {
372
+ muted: true,
373
+ size: 1,
374
+ children: message
375
+ })
376
+ }),
377
+ fallbackPlacements: ["right", "left"],
378
+ placement: "top",
379
+ portal: true,
380
+ children: /* @__PURE__ */jsx(Button, {
381
+ onClick: translation ? handleOpen : handleCreate,
382
+ mode: current && disabled ? "default" : "bleed",
383
+ disabled: disabled || current,
384
+ children: /* @__PURE__ */jsxs(Flex, {
385
+ gap: 3,
386
+ align: "center",
387
+ children: [disabled ? /* @__PURE__ */jsx(Spinner, {}) : /* @__PURE__ */jsx(Text, {
388
+ size: 2,
389
+ children: translation ? /* @__PURE__ */jsx(SplitVerticalIcon, {}) : current ? /* @__PURE__ */jsx(CheckmarkIcon, {}) : /* @__PURE__ */jsx(AddIcon, {})
390
+ }), /* @__PURE__ */jsx(Box, {
391
+ flex: 1,
392
+ children: /* @__PURE__ */jsx(Text, {
393
+ children: language.title
394
+ })
395
+ }), /* @__PURE__ */jsx(Badge, {
396
+ tone: disabled || current ? "default" : "primary",
397
+ children: language.id
398
+ })]
399
+ })
238
400
  })
239
401
  });
240
402
  }
241
- function useTranslationMetadata(id, schemaType) {
242
- const query = "*[_type == $translationSchema && $id in translations[].value._ref][0]";
243
- const {
244
- data,
245
- loading,
246
- error
247
- } = useListeningQuery(query, {
248
- params: {
249
- id,
250
- translationSchema: METADATA_SCHEMA_NAME
251
- }
252
- });
253
- return {
254
- data,
255
- loading,
256
- error
257
- };
258
- }
259
- function LanguageManage(props) {
260
- const {
261
- id
262
- } = props;
263
- const open = useOpenInNewPane(id, METADATA_SCHEMA_NAME);
264
- return /* @__PURE__ */jsx(Button, {
265
- disabled: !id,
266
- mode: "ghost",
267
- text: "Manage Translations",
268
- icon: CogIcon,
269
- onClick: () => open()
270
- });
271
- }
272
403
  function LanguagePatch(props) {
273
404
  const {
274
405
  apiVersion = API_VERSION,
@@ -310,12 +441,25 @@ function LanguagePatch(props) {
310
441
  });
311
442
  }, [source, documentId, client, languageField, language, schemaType, toast]);
312
443
  return /* @__PURE__ */jsx(Button, {
313
- mode: "ghost",
314
- text: language.title,
315
- icon: ChevronRightIcon,
316
- onClick: () => handleClick(),
444
+ mode: "bleed",
445
+ onClick: handleClick,
317
446
  disabled,
318
- justify: "flex-start"
447
+ justify: "flex-start",
448
+ children: /* @__PURE__ */jsxs(Flex, {
449
+ gap: 3,
450
+ align: "center",
451
+ children: [/* @__PURE__ */jsx(Text, {
452
+ size: 2,
453
+ children: /* @__PURE__ */jsx(EditIcon, {})
454
+ }), /* @__PURE__ */jsx(Box, {
455
+ flex: 1,
456
+ children: /* @__PURE__ */jsx(Text, {
457
+ children: language.title
458
+ })
459
+ }), /* @__PURE__ */jsx(Badge, {
460
+ children: language.id
461
+ })]
462
+ })
319
463
  });
320
464
  }
321
465
  function MenuButton(props) {
@@ -372,7 +516,34 @@ function MenuButton(props) {
372
516
  padding: 1,
373
517
  space: 1,
374
518
  children: [supportedLanguages.length > 0 ? /* @__PURE__ */jsxs(Fragment, {
375
- children: [supportedLanguages.map((language, langIndex) => {
519
+ children: [loading ? null : /* @__PURE__ */jsxs(Fragment, {
520
+ children: [allLanguagesAreValid ? null : /* @__PURE__ */jsx(Card, {
521
+ tone: "caution",
522
+ padding: 3,
523
+ children: /* @__PURE__ */jsx(Text, {
524
+ size: 1,
525
+ children: "Not all language objects are valid. See the console."
526
+ })
527
+ }), sourceLanguageId ? null : /* @__PURE__ */jsx(Card, {
528
+ tone: "caution",
529
+ padding: 3,
530
+ children: /* @__PURE__ */jsxs(Text, {
531
+ size: 1,
532
+ children: ["Choose a language to ", /* @__PURE__ */jsx("br", {}), "apply to ", /* @__PURE__ */jsx("strong", {
533
+ children: "this Document"
534
+ })]
535
+ })
536
+ }), sourceLanguageId && !sourceLanguageIsValid ? /* @__PURE__ */jsx(Card, {
537
+ tone: "caution",
538
+ padding: 3,
539
+ children: /* @__PURE__ */jsxs(Text, {
540
+ size: 1,
541
+ children: ["Select a supported language.", /* @__PURE__ */jsx("br", {}), "Current language value: ", /* @__PURE__ */jsx("code", {
542
+ children: sourceLanguageId
543
+ })]
544
+ })
545
+ }) : null]
546
+ }), supportedLanguages.map((language, langIndex) => {
376
547
  var _a;
377
548
  return !loading && sourceLanguageId && sourceLanguageIsValid ?
378
549
  // Button to duplicate this document to a new translation
@@ -404,39 +575,13 @@ function MenuButton(props) {
404
575
  return ((_a2 = t == null ? void 0 : t.value) == null ? void 0 : _a2._ref) !== documentId;
405
576
  }).some(t => t._key === language.id))) != null ? _a : false
406
577
  }, language.id || language.title || "lang-".concat(langIndex));
407
- }), loading ? null : /* @__PURE__ */jsxs(Fragment, {
408
- children: [allLanguagesAreValid ? null : /* @__PURE__ */jsx(Card, {
409
- tone: "caution",
410
- padding: 3,
411
- children: /* @__PURE__ */jsx(Text, {
412
- size: 1,
413
- children: "Not all language objects are valid. See the console."
414
- })
415
- }), sourceLanguageId ? null : /* @__PURE__ */jsx(Card, {
416
- tone: "caution",
417
- padding: 3,
418
- children: /* @__PURE__ */jsxs(Text, {
419
- size: 1,
420
- children: ["Choose a language to ", /* @__PURE__ */jsx("br", {}), "apply to ", /* @__PURE__ */jsx("strong", {
421
- children: "this Document"
422
- })]
423
- })
424
- }), sourceLanguageId && !sourceLanguageIsValid ? /* @__PURE__ */jsx(Card, {
425
- tone: "caution",
426
- padding: 3,
427
- children: /* @__PURE__ */jsxs(Text, {
428
- size: 1,
429
- children: ["Change the current language value ", /* @__PURE__ */jsx("code", {
430
- children: sourceLanguageId
431
- }), /* @__PURE__ */jsx("br", {}), "to one of the supported languages"]
432
- })
433
- }) : null]
434
578
  })]
435
579
  }) : null, /* @__PURE__ */jsx(LanguageManage, {
436
580
  id: metadata == null ? void 0 : metadata._id
437
581
  })]
438
582
  })
439
583
  });
584
+ const issueWithTranslations = !loading && sourceLanguageId && !sourceLanguageIsValid;
440
585
  return /* @__PURE__ */jsx(Popover, {
441
586
  constrainSize: true,
442
587
  content,
@@ -448,7 +593,7 @@ function MenuButton(props) {
448
593
  text: "Translations",
449
594
  mode: "bleed",
450
595
  disabled: !source,
451
- tone: !source || !loading && sourceLanguageId && sourceLanguageIsValid ? void 0 : "caution",
596
+ tone: !source || loading || !issueWithTranslations ? void 0 : "caution",
452
597
  icon: TranslateIcon,
453
598
  onClick: handleClick,
454
599
  ref: setButton,
@@ -456,20 +601,102 @@ function MenuButton(props) {
456
601
  })
457
602
  });
458
603
  }
459
- function LanguageBadge(props, supportedLanguages, languageField) {
460
- var _a, _b;
461
- const source = (props == null ? void 0 : props.draft) || (props == null ? void 0 : props.published);
462
- const languageId = source == null ? void 0 : source[languageField];
463
- if (!languageId) {
604
+ function ReferencePatcher(props) {
605
+ const {
606
+ translation,
607
+ documentType,
608
+ metadataId
609
+ } = props;
610
+ const editState = useEditState(translation.value._ref, documentType);
611
+ const client = useClient({
612
+ apiVersion: API_VERSION
613
+ });
614
+ const {
615
+ onChange
616
+ } = useDocumentPane();
617
+ React.useEffect(() => {
618
+ if (
619
+ // We have a reference
620
+ translation.value._ref &&
621
+ // It's still weak and not-yet-strengthened
622
+ translation.value._weak && translation.value._strengthenOnPublish &&
623
+ // The referenced document has just been published
624
+ !editState.draft && editState.published && editState.ready) {
625
+ const referencePathBase = ["translations", {
626
+ _key: translation._key
627
+ }, "value"];
628
+ onChange(new PatchEvent([unset([...referencePathBase, "_weak"]), unset([...referencePathBase, "_strengthenOnPublish"])]));
629
+ }
630
+ }, [translation, editState, metadataId, client, onChange]);
631
+ return null;
632
+ }
633
+ function OptimisticallyStrengthen(props) {
634
+ const {
635
+ translations = [],
636
+ metadataId
637
+ } = props;
638
+ if (!translations.length) {
464
639
  return null;
465
640
  }
466
- const language = Array.isArray(supportedLanguages) ? supportedLanguages.find(l => l.id === languageId) : null;
467
- return {
468
- label: (_a = language == null ? void 0 : language.id) != null ? _a : String(languageId),
469
- title: (_b = language == null ? void 0 : language.title) != null ? _b : void 0,
470
- color: "primary"
471
- };
641
+ return /* @__PURE__ */jsx(Fragment, {
642
+ children: translations.map(translation => {
643
+ var _a;
644
+ return ((_a = translation.value._strengthenOnPublish) == null ? void 0 : _a.type) ? /* @__PURE__ */jsx(ReferencePatcher, {
645
+ translation,
646
+ documentType: translation.value._strengthenOnPublish.type,
647
+ metadataId
648
+ }, translation._key) : null;
649
+ })
650
+ });
472
651
  }
652
+ var metadata = schemaTypes => defineType({
653
+ type: "document",
654
+ name: METADATA_SCHEMA_NAME,
655
+ title: "Translation metadata",
656
+ icon: TranslateIcon,
657
+ liveEdit: true,
658
+ fields: [defineField({
659
+ name: "translations",
660
+ type: "internationalizedArrayReference"
661
+ }), defineField({
662
+ name: "schemaTypes",
663
+ description: "Optional: Used to filter the reference fields above so all translations share the same types.",
664
+ type: "array",
665
+ // For some reason TS dislikes this line because of the DocumentDefinition return type
666
+ // @ts-expect-error
667
+ of: [{
668
+ type: "string"
669
+ }],
670
+ options: {
671
+ list: schemaTypes
672
+ },
673
+ readOnly: _ref => {
674
+ let {
675
+ value
676
+ } = _ref;
677
+ return Boolean(value);
678
+ }
679
+ })],
680
+ preview: {
681
+ select: {
682
+ translations: "translations",
683
+ documentSchemaTypes: "schemaTypes"
684
+ },
685
+ prepare(selection) {
686
+ const {
687
+ translations,
688
+ documentSchemaTypes
689
+ } = selection;
690
+ const title = translations.length === 1 ? "1 Translation" : "".concat(translations.length, " Translations");
691
+ const languageKeys = translations.length ? translations.map(t => t._key.toUpperCase()).join(", ") : "";
692
+ const subtitle = [languageKeys ? "(".concat(languageKeys, ")") : null, (documentSchemaTypes == null ? void 0 : documentSchemaTypes.length) ? documentSchemaTypes.map(s => s.toUpperCase()).join(", ") : "No Schemas Defined"].filter(Boolean).join(" ");
693
+ return {
694
+ title,
695
+ subtitle
696
+ };
697
+ }
698
+ }
699
+ });
473
700
  const DEFAULT_CONFIG = {
474
701
  supportedLanguages: [],
475
702
  schemaTypes: [],
@@ -502,10 +729,22 @@ const documentInternationalization = definePlugin(config => {
502
729
  form: {
503
730
  components: {
504
731
  input: props => {
505
- if (bulkPublish && props.id === "root" && props.schemaType.name === METADATA_SCHEMA_NAME) {
506
- return /* @__PURE__ */jsx(Stack, {
732
+ var _a, _b, _c;
733
+ if (props.id === "root" && props.schemaType.name === METADATA_SCHEMA_NAME && isSanityDocument(props == null ? void 0 : props.value)) {
734
+ const metadataId = (_a = props == null ? void 0 : props.value) == null ? void 0 : _a._id;
735
+ const translations = (_c = (_b = props == null ? void 0 : props.value) == null ? void 0 : _b.translations) != null ? _c : [];
736
+ const weakAndTypedTranslations = translations.filter(t => {
737
+ var _a2, _b2;
738
+ return ((_a2 = t == null ? void 0 : t.value) == null ? void 0 : _a2._weak) && ((_b2 = t.value) == null ? void 0 : _b2._strengthenOnPublish);
739
+ });
740
+ return /* @__PURE__ */jsxs(Stack, {
507
741
  space: 5,
508
- children: props.renderDefault(props)
742
+ children: [bulkPublish ? /* @__PURE__ */jsx(BulkPublish, {
743
+ translations
744
+ }) : null, weakAndTypedTranslations.length > 0 ? /* @__PURE__ */jsx(OptimisticallyStrengthen, {
745
+ metadataId,
746
+ translations: weakAndTypedTranslations
747
+ }) : null, props.renderDefault(props)]
509
748
  });
510
749
  }
511
750
  return props.renderDefault(props);
@@ -591,22 +830,34 @@ const documentInternationalization = definePlugin(config => {
591
830
  // It cuts down on attribute usage and gives UI conveniences to add new translations
592
831
  internationalizedArray({
593
832
  languages: supportedLanguages,
594
- fieldTypes: [
595
- // TODO: The plugin should allow this kind of input
596
- // @ts-ignore
597
- defineField({
833
+ fieldTypes: [defineField({
598
834
  name: "reference",
599
835
  type: "reference",
600
836
  to: schemaTypes.map(type => ({
601
837
  type
602
838
  })),
603
- // TODO: Add a validation rule to *ensure* the document's language matches the array key
604
839
  // Reference filters don't actually enforce validation!
605
- // validation: (Rule) => Rule.custom(),
840
+ validation: Rule => Rule.custom(async (item, context) => {
841
+ var _a;
842
+ if (!((_a = item == null ? void 0 : item.value) == null ? void 0 : _a._ref) || !(item == null ? void 0 : item._key)) {
843
+ return true;
844
+ }
845
+ const client = context.getClient({
846
+ apiVersion: API_VERSION
847
+ });
848
+ const valueLanguage = await client.fetch("*[_id in [$ref, $draftRef]][0].".concat(languageField), {
849
+ ref: item.value._ref,
850
+ draftRef: "drafts.".concat(item.value._ref)
851
+ });
852
+ if (valueLanguage && valueLanguage === item._key) {
853
+ return true;
854
+ }
855
+ return "Referenced document does not have the correct language value";
856
+ }),
606
857
  options: {
607
858
  collapsed: false,
608
859
  // TODO: Update type once it knows the values of this filter
609
- // @ts-ignore
860
+ // @ts-expect-error
610
861
  filter: _ref5 => {
611
862
  let {
612
863
  parent,