@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/README.md +6 -3
- package/lib/index.esm.js +407 -156
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +405 -154
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/BulkPublish/DocumentCheck.tsx +39 -16
- package/src/components/BulkPublish/index.tsx +86 -55
- package/src/components/LanguageManage.tsx +1 -2
- package/src/components/LanguageOption.tsx +78 -28
- package/src/components/LanguagePatch.tsx +16 -8
- package/src/components/MenuButton.tsx +76 -43
- package/src/components/OptimisticallyStrengthen/ReferencePatcher.tsx +49 -0
- package/src/components/OptimisticallyStrengthen/index.tsx +34 -0
- package/src/plugin.tsx +195 -147
- package/src/schema/translation/metadata.ts +1 -1
- package/src/types.ts +9 -7
package/lib/index.esm.js
CHANGED
|
@@ -1,63 +1,172 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
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 {
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
|
|
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: "
|
|
314
|
-
|
|
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: [
|
|
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 ||
|
|
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
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
-
|
|
506
|
-
|
|
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:
|
|
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
|
-
|
|
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-
|
|
860
|
+
// @ts-expect-error
|
|
610
861
|
filter: _ref5 => {
|
|
611
862
|
let {
|
|
612
863
|
parent,
|