@sanity/document-internationalization 2.0.0-studio-v3-plugin-v2.4 → 2.0.0-studio-v3-plugin-v2.6
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/LICENSE +1 -1
- package/lib/{src/index.d.ts → index.d.ts} +3 -1
- package/lib/index.esm.js +643 -1
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +654 -1
- package/lib/index.js.map +1 -1
- package/package.json +48 -33
- package/src/components/MenuButton.tsx +2 -2
- package/src/schema/translation/metadata.ts +10 -6
- package/src/types.ts +4 -2
package/LICENSE
CHANGED
|
@@ -15,6 +15,8 @@ declare type PluginConfig = {
|
|
|
15
15
|
bulkPublish?: boolean
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
declare type SupportedLanguages =
|
|
18
|
+
declare type SupportedLanguages =
|
|
19
|
+
| Language[]
|
|
20
|
+
| ((client: SanityClient) => Promise<Language[]>)
|
|
19
21
|
|
|
20
22
|
export {}
|
package/lib/index.esm.js
CHANGED
|
@@ -1 +1,643 @@
|
|
|
1
|
-
function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function t(t){for(var a=1;a<arguments.length;a++){var r=null!=arguments[a]?arguments[a]:{};a%2?e(Object(r),!0).forEach((function(e){n(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}function n(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var a=n.call(e,t||"default");if("object"!=typeof a)return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}import{jsx as a,jsxs as r,Fragment as i}from"react/jsx-runtime";import{defineType as o,defineField as l,useClient as s,useEditState as c,definePlugin as u}from"sanity";import{internationalizedArray as d}from"sanity-plugin-internationalized-array";import{useToast as p,Button as g,Flex as f,Spinner as m,Text as h,Box as y,Badge as v,useClickOutside as b,Card as T,Stack as _,Popover as O}from"@sanity/ui";import{TranslateIcon as j,SplitVerticalIcon as w,CheckmarkIcon as k,AddIcon as I,CogIcon as P,ChevronRightIcon as S}from"@sanity/icons";import x,{useCallback as C,useState as L}from"react";import{uuid as F}from"@sanity/uuid";import{usePaneRouter as A}from"sanity/desk";import{RouterContext as z}from"sanity/router";import{useListeningQuery as D}from"sanity-plugin-utils";const E="translation.metadata",M="2022-11-27";var N=e=>o({type:"document",name:E,title:"Translation metadata",icon:j,liveEdit:!0,fields:[l({name:"translations",type:"internationalizedArrayReference"}),l({name:"schemaTypes",description:"Used to filter the reference fields above so all translations share the same types.",type:"array",of:[{type:"string"}],options:{list:e},readOnly:e=>{let{value:t}=e;return Boolean(t)}})],preview:{select:{translations:"translations",documentSchemaTypes:"schemaTypes"},prepare(e){const{translations:t,documentSchemaTypes:n}=e,a=1===t.length?"1 Translation":"".concat(t.length," Translations"),r=t.length?t.map((e=>e._key.toUpperCase())).join(", "):"";return{title:a,subtitle:[r?"(".concat(r,")"):null,(null==n?void 0:n.length)?n.map((e=>e.toUpperCase())).join(", "):"No Schemas Defined"].filter(Boolean).join(" ")}}}});function V(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:(e,t)=>e===t;if(e===t)return!0;if(!e||!t)return!1;const a=e.length;if(t.length!==a)return!1;for(let r=0;r<a;r++)if(!n(e[r],t[r]))return!1;return!0}const $=[];const U=(e,t,n)=>function(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};for(const e of $)if(V(t,e.keys,e.equal)){if(n)return;if(Object.prototype.hasOwnProperty.call(e,"error"))throw e.error;if(Object.prototype.hasOwnProperty.call(e,"response"))return e.response;if(!n)throw e.promise}const r={keys:t,equal:a.equal,promise:e(...t).then((e=>r.response=e)).then((()=>{a.lifespan&&a.lifespan>0&&setTimeout((()=>{const e=$.indexOf(r);-1!==e&&$.splice(e,1)}),a.lifespan)})).catch((e=>r.error=e))};if($.push(r),!n)throw r.promise}(e,t,!1,n);function B(e,t){const n=x.useContext(z),{routerPanesState:a,groupIndex:r}=A();return x.useCallback((()=>{if(!n||!e||!t)return;const i=[...a];i.splice(r+1,0,[{id:e,params:{type:t}}]);const o=n.resolvePathFromState({panes:i});n.navigateUrl({path:o})}),[e,t,n,a,r])}function q(e,t,n){return{_key:e,value:{_type:"reference",_ref:t,_weak:!0,_strengthenOnPublish:{type:n}}}}function R(e){var n;const{apiVersion:i=M,index:o,language:l,languageField:c,schemaType:u,documentId:d,disabled:b,current:T,sourceId:_,sourceLanguageId:O,metadata:j,translation:P}=e,S=s({apiVersion:i}),x=p(),L=B(null==(n=null==P?void 0:P.value)?void 0:n._ref,u),A=C((()=>L()),[L]),z=C((async()=>{var e;const n=Boolean(null==j?void 0:j._id),a=S.transaction(),r=d.startsWith("drafts.")?[d,d.replace("drafts.","")]:[d,"drafts.".concat(d)],i=t(t({},await S.fetch("*[_id in $ids]|order(_updatedAt desc)[0]",{ids:r})),{},{_id:"drafts.".concat(F()),[c]:l.id});a.create(i);const s=null!=(e=null==j?void 0:j._id)?e:F(),p=q(l.id,i._id.replace("drafts.",""),u);if(n){const e="translations[".concat(o-1,"]"),t=S.patch(s).setIfMissing({translations:[]}).insert("before",e,[p]);a.patch(t)}else{const e=O?q(O,_,u):null;a.createIfNotExists({_id:s,_type:E,schemaTypes:[u],translations:[p,e].filter(Boolean)})}a.commit().then((()=>x.push({status:"success",title:"Created ".concat(l.title," translation"),description:n?"Updated Translations Metadata":"Created Translations Metadata"}))).catch((e=>(console.error(e),x.push({status:"error",title:"Error creating translation",description:e.message}))))}),[S,d,o,l,c,null==j?void 0:j._id,u,_,O,x]);return a(g,{onClick:P?A:z,mode:T?"default":"bleed",disabled:b||T,children:r(f,{gap:3,align:"center",children:[b?a(m,{}):a(h,{size:2,children:a(P?w:T?k:I,{})}),a(y,{flex:1,children:a(h,{children:l.title})}),a(v,{tone:b||T?"default":"primary",children:l.id})]})})}function W(e){const{id:t}=e,n=B(t,E);return a(g,{disabled:!t,mode:"ghost",text:"Manage Translations",icon:P,onClick:()=>n()})}function G(e){const{apiVersion:t=M,language:n,languageField:r,documentId:i,schemaType:o,source:l,disabled:c=!1}=e,u=s({apiVersion:t}),d=p(),f=x.useCallback((()=>{const e=l?l._id:"draft.".concat(i),t=u.transaction();l||t.createIfNotExists({_id:e,_type:o});const a=u.patch(e).set({[r]:n.id});t.patch(a),t.commit().then((()=>{d.push({title:"Set document language to ".concat(n.title),status:"success"})})).catch((e=>(console.error(e),d.push({title:"Failed to set document language to ".concat(n.title),status:"error"}))))}),[l,i,u,r,n,o,d]);return a(g,{mode:"ghost",text:n.title,icon:S,onClick:()=>f(),disabled:c,justify:"flex-start"})}function H(e){const{apiVersion:t=M,schemaType:n,documentId:o,languageField:l}=e,u=s({apiVersion:t}),d=Array.isArray(e.supportedLanguages)?e.supportedLanguages:U((async()=>"function"==typeof e.supportedLanguages?e.supportedLanguages(u):e.supportedLanguages),[]),[p,f]=L(!1),m=C((()=>f((e=>!e))),[]),[v,w]=L(null),[k,I]=L(null),P=C((()=>f(!1)),[]);b(P,[v,k]);const{data:S,loading:F,error:A}=function(e,t){const{data:n,loading:a,error:r}=D("*[_type == $translationSchema && $id in translations[].value._ref][0]",{params:{id:e,translationSchema:E}});return{data:n,loading:a,error:r}}(o),{draft:z,published:N}=c(o,n),V=z||N,$=null==V?void 0:V[l],B=d.some((e=>e.id===$)),q=x.useMemo((()=>{const e=d.every((e=>e.id&&e.title));return e||console.warn('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.',d),e}),[d]),H=a(y,{overflow:"auto",children:A?a(T,{tone:"critical",padding:2,children:r(h,{children:["Error: ",A]})}):r(_,{padding:1,space:1,children:[d.length>0?r(i,{children:[d.map(((e,t)=>{var r;return!F&&$&&B?a(R,{index:t,language:e,languageField:l,schemaType:n,documentId:o,disabled:F||!q,current:e.id===$,metadata:S,sourceId:o,sourceLanguageId:$,translation:null==S?void 0:S.translations.find((t=>t._key===e.id))},e.id||e.title||"lang-".concat(t)):a(G,{languageField:l,source:V,documentId:o,schemaType:n,language:e,disabled:null!=(r=!q||(null==S?void 0:S.translations.filter((e=>{var t;return(null==(t=null==e?void 0:e.value)?void 0:t._ref)!==o})).some((t=>t._key===e.id))))&&r},e.id||e.title||"lang-".concat(t))})),F?null:r(i,{children:[q?null:a(T,{tone:"caution",padding:3,children:a(h,{size:1,children:"Not all language objects are valid. See the console."})}),$?null:a(T,{tone:"caution",padding:3,children:r(h,{size:1,children:["Choose a language to ",a("br",{}),"apply to ",a("strong",{children:"this Document"})]})}),$&&!B?a(T,{tone:"caution",padding:3,children:r(h,{size:1,children:["Change the current language value ",a("code",{children:$}),a("br",{}),"to one of the supported languages"]})}):null]})]}):null,a(W,{id:null==S?void 0:S._id})]})});return a(O,{constrainSize:!0,content:H,open:p,portal:!0,ref:I,children:a(g,{text:"Translations",mode:"bleed",disabled:!V,tone:!V||!F&&$&&B?void 0:"caution",icon:j,onClick:m,ref:w,selected:p})})}const J={supportedLanguages:[],schemaTypes:[],languageField:"language",bulkPublish:!1},K=u((e=>{const{supportedLanguages:n,schemaTypes:r,languageField:i,bulkPublish:o}=t(t({},J),e);return{name:"@sanity/document-internationalization",form:{components:{input:e=>o&&"root"===e.id&&e.schemaType.name===E?a(_,{space:5,children:e.renderDefault(e)}):e.renderDefault(e)}},document:{unstable_languageFilter:(e,t)=>{const{schemaType:o,documentId:l}=t;return r.includes(o)?[...e,()=>((e,t)=>a(H,{supportedLanguages:n,schemaType:e,documentId:null!=t?t:"",languageField:i}))(o,l)]:e},badges:(e,t)=>{let{schemaType:a}=t;return r.includes(a)?[e=>function(e,t,n){var a,r;const i=(null==e?void 0:e.draft)||(null==e?void 0:e.published),o=null==i?void 0:i[n];if(!o)return null;const l=Array.isArray(t)?t.find((e=>e.id===o)):null;return{label:null!=(a=null==l?void 0:l.id)?a:String(o),title:null!=(r=null==l?void 0:l.title)?r:void 0,color:"primary"}}(e,n,i),...e]:e}},schema:{types:[N(r)],templates:(e,t)=>{let{schema:a}=t;if(!Array.isArray(n))return e;const o=r.map((e=>{var t,n;return{id:"".concat(e,"-parameterized"),title:"".concat(null!=(n=null==(t=null==a?void 0:a.get(e))?void 0:t.title)?n:e,": with Language"),schemaType:e,parameters:[{name:"languageId",title:"Language ID",type:"string"}],value:e=>{let{languageId:t}=e;return{[i]:t}}}})),l=r.flatMap((e=>n.map((t=>{var n,r;return{id:"".concat(e,"-").concat(t.id),title:"".concat(t.title," ").concat(null!=(r=null==(n=null==a?void 0:a.get(e))?void 0:n.title)?r:e),schemaType:e,value:{[i]:t.id}}}))));return[...e,...o,...l]}},plugins:[d({languages:n,fieldTypes:[l({name:"reference",type:"reference",to:r.map((e=>({type:e}))),options:{collapsed:!1,filter:e=>{let{parent:t,document:n}=e;if(!t)return null;const a=(Array.isArray(t)?t:[t]).find((e=>e._key));return(null==a?void 0:a._key)?n.schemaTypes?{filter:"_type in $schemaTypes && ".concat(i," == $language"),params:{schemaTypes:n.schemaTypes,language:a._key}}:{filter:"".concat(i," == $language"),params:{language:a._key}}:null}}},{strict:!1})]})]}}));export{K as documentInternationalization};//# sourceMappingURL=index.esm.js.map
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { defineType, defineField, useClient, useEditState, definePlugin } from 'sanity';
|
|
3
|
+
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
|
+
import React, { useCallback, useState } from 'react';
|
|
7
|
+
import { uuid } from '@sanity/uuid';
|
|
8
|
+
import { usePaneRouter } from 'sanity/desk';
|
|
9
|
+
import { RouterContext } from 'sanity/router';
|
|
10
|
+
import { useListeningQuery } from 'sanity-plugin-utils';
|
|
11
|
+
const METADATA_SCHEMA_NAME = "translation.metadata";
|
|
12
|
+
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);
|
|
39
|
+
}
|
|
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
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
function shallowEqualArrays(arrA, arrB) {
|
|
62
|
+
let equal = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : (a, b) => a === b;
|
|
63
|
+
if (arrA === arrB) return true;
|
|
64
|
+
if (!arrA || !arrB) return false;
|
|
65
|
+
const len = arrA.length;
|
|
66
|
+
if (arrB.length !== len) return false;
|
|
67
|
+
for (let i = 0; i < len; i++) if (!equal(arrA[i], arrB[i])) return false;
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
const globalCache = [];
|
|
71
|
+
function query(fn, keys) {
|
|
72
|
+
let preload = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
73
|
+
let config = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
74
|
+
for (const entry of globalCache) {
|
|
75
|
+
// Find a match
|
|
76
|
+
if (shallowEqualArrays(keys, entry.keys, entry.equal)) {
|
|
77
|
+
// If we're pre-loading and the element is present, just return
|
|
78
|
+
if (preload) return undefined; // If an error occurred, throw
|
|
79
|
+
|
|
80
|
+
if (Object.prototype.hasOwnProperty.call(entry, 'error')) throw entry.error; // If a response was successful, return
|
|
81
|
+
|
|
82
|
+
if (Object.prototype.hasOwnProperty.call(entry, 'response')) return entry.response; // If the promise is still unresolved, throw
|
|
83
|
+
|
|
84
|
+
if (!preload) throw entry.promise;
|
|
85
|
+
}
|
|
86
|
+
} // The request is new or has changed.
|
|
87
|
+
|
|
88
|
+
const entry = {
|
|
89
|
+
keys,
|
|
90
|
+
equal: config.equal,
|
|
91
|
+
promise:
|
|
92
|
+
// Execute the promise
|
|
93
|
+
fn(...keys) // When it resolves, store its value
|
|
94
|
+
.then(response => entry.response = response) // Remove the entry if a lifespan was given
|
|
95
|
+
.then(() => {
|
|
96
|
+
if (config.lifespan && config.lifespan > 0) {
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
const index = globalCache.indexOf(entry);
|
|
99
|
+
if (index !== -1) globalCache.splice(index, 1);
|
|
100
|
+
}, config.lifespan);
|
|
101
|
+
}
|
|
102
|
+
}) // Store caught errors, they will be thrown in the render-phase to bubble into an error-bound
|
|
103
|
+
.catch(error => entry.error = error)
|
|
104
|
+
}; // Register the entry
|
|
105
|
+
|
|
106
|
+
globalCache.push(entry); // And throw the promise, this yields control back to React
|
|
107
|
+
|
|
108
|
+
if (!preload) throw entry.promise;
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const suspend = (fn, keys, config) => query(fn, keys, false, config);
|
|
112
|
+
function useOpenInNewPane(id, type) {
|
|
113
|
+
const routerContext = React.useContext(RouterContext);
|
|
114
|
+
const {
|
|
115
|
+
routerPanesState,
|
|
116
|
+
groupIndex
|
|
117
|
+
} = usePaneRouter();
|
|
118
|
+
const openInNewPane = React.useCallback(() => {
|
|
119
|
+
if (!routerContext || !id || !type) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const panes = [...routerPanesState];
|
|
123
|
+
panes.splice(groupIndex + 1, 0, [{
|
|
124
|
+
id,
|
|
125
|
+
params: {
|
|
126
|
+
type
|
|
127
|
+
}
|
|
128
|
+
}]);
|
|
129
|
+
const href = routerContext.resolvePathFromState({
|
|
130
|
+
panes
|
|
131
|
+
});
|
|
132
|
+
routerContext.navigateUrl({
|
|
133
|
+
path: href
|
|
134
|
+
});
|
|
135
|
+
}, [id, type, routerContext, routerPanesState, groupIndex]);
|
|
136
|
+
return openInNewPane;
|
|
137
|
+
}
|
|
138
|
+
function createReference(key, ref, type) {
|
|
139
|
+
return {
|
|
140
|
+
_key: key,
|
|
141
|
+
value: {
|
|
142
|
+
_type: "reference",
|
|
143
|
+
_ref: ref,
|
|
144
|
+
_weak: true,
|
|
145
|
+
_strengthenOnPublish: {
|
|
146
|
+
type
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function LanguageOption(props) {
|
|
152
|
+
var _a;
|
|
153
|
+
const {
|
|
154
|
+
apiVersion = API_VERSION,
|
|
155
|
+
index,
|
|
156
|
+
language,
|
|
157
|
+
languageField,
|
|
158
|
+
schemaType,
|
|
159
|
+
documentId,
|
|
160
|
+
disabled,
|
|
161
|
+
current,
|
|
162
|
+
sourceId,
|
|
163
|
+
sourceLanguageId,
|
|
164
|
+
metadata,
|
|
165
|
+
translation
|
|
166
|
+
} = props;
|
|
167
|
+
const client = useClient({
|
|
168
|
+
apiVersion
|
|
169
|
+
});
|
|
170
|
+
const toast = useToast();
|
|
171
|
+
const open = useOpenInNewPane((_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref, schemaType);
|
|
172
|
+
const handleOpen = useCallback(() => open(), [open]);
|
|
173
|
+
const handleCreate = useCallback(async () => {
|
|
174
|
+
var _a2;
|
|
175
|
+
const metadataExists = Boolean(metadata == null ? void 0 : metadata._id);
|
|
176
|
+
const transaction = client.transaction();
|
|
177
|
+
const documentIds = documentId.startsWith("drafts.") ? [documentId, documentId.replace("drafts.", "")] : [documentId, "drafts.".concat(documentId)];
|
|
178
|
+
const latestDocument = await client.fetch("*[_id in $ids]|order(_updatedAt desc)[0]", {
|
|
179
|
+
ids: documentIds
|
|
180
|
+
});
|
|
181
|
+
const newTranslationDocument = {
|
|
182
|
+
...latestDocument,
|
|
183
|
+
_id: "drafts.".concat(uuid()),
|
|
184
|
+
[languageField]: language.id
|
|
185
|
+
};
|
|
186
|
+
transaction.create(newTranslationDocument);
|
|
187
|
+
const metadataId = (_a2 = metadata == null ? void 0 : metadata._id) != null ? _a2 : uuid();
|
|
188
|
+
const newTranslationReference = createReference(language.id, newTranslationDocument._id.replace("drafts.", ""), schemaType);
|
|
189
|
+
if (metadataExists) {
|
|
190
|
+
const path = "translations[".concat(index - 1, "]");
|
|
191
|
+
const metadataPatch = client.patch(metadataId).setIfMissing({
|
|
192
|
+
translations: []
|
|
193
|
+
}).insert("before", path, [newTranslationReference]);
|
|
194
|
+
transaction.patch(metadataPatch);
|
|
195
|
+
} else {
|
|
196
|
+
const sourceReference = sourceLanguageId ? createReference(sourceLanguageId, sourceId, schemaType) : null;
|
|
197
|
+
transaction.createIfNotExists({
|
|
198
|
+
_id: metadataId,
|
|
199
|
+
_type: METADATA_SCHEMA_NAME,
|
|
200
|
+
schemaTypes: [schemaType],
|
|
201
|
+
translations: [newTranslationReference, sourceReference].filter(Boolean)
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
transaction.commit().then(() => {
|
|
205
|
+
return toast.push({
|
|
206
|
+
status: "success",
|
|
207
|
+
title: "Created ".concat(language.title, " translation"),
|
|
208
|
+
description: metadataExists ? "Updated Translations Metadata" : "Created Translations Metadata"
|
|
209
|
+
});
|
|
210
|
+
}).catch(err => {
|
|
211
|
+
console.error(err);
|
|
212
|
+
return toast.push({
|
|
213
|
+
status: "error",
|
|
214
|
+
title: "Error creating translation",
|
|
215
|
+
description: err.message
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}, [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
|
+
})]
|
|
238
|
+
})
|
|
239
|
+
});
|
|
240
|
+
}
|
|
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
|
+
function LanguagePatch(props) {
|
|
273
|
+
const {
|
|
274
|
+
apiVersion = API_VERSION,
|
|
275
|
+
language,
|
|
276
|
+
languageField,
|
|
277
|
+
documentId,
|
|
278
|
+
schemaType,
|
|
279
|
+
source,
|
|
280
|
+
disabled = false
|
|
281
|
+
} = props;
|
|
282
|
+
const client = useClient({
|
|
283
|
+
apiVersion
|
|
284
|
+
});
|
|
285
|
+
const toast = useToast();
|
|
286
|
+
const handleClick = React.useCallback(() => {
|
|
287
|
+
const currentId = source ? source._id : "draft.".concat(documentId);
|
|
288
|
+
const transaction = client.transaction();
|
|
289
|
+
if (!source) {
|
|
290
|
+
transaction.createIfNotExists({
|
|
291
|
+
_id: currentId,
|
|
292
|
+
_type: schemaType
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const patch = client.patch(currentId).set({
|
|
296
|
+
[languageField]: language.id
|
|
297
|
+
});
|
|
298
|
+
transaction.patch(patch);
|
|
299
|
+
transaction.commit().then(() => {
|
|
300
|
+
toast.push({
|
|
301
|
+
title: "Set document language to ".concat(language.title),
|
|
302
|
+
status: "success"
|
|
303
|
+
});
|
|
304
|
+
}).catch(err => {
|
|
305
|
+
console.error(err);
|
|
306
|
+
return toast.push({
|
|
307
|
+
title: "Failed to set document language to ".concat(language.title),
|
|
308
|
+
status: "error"
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
}, [source, documentId, client, languageField, language, schemaType, toast]);
|
|
312
|
+
return /* @__PURE__ */jsx(Button, {
|
|
313
|
+
mode: "ghost",
|
|
314
|
+
text: language.title,
|
|
315
|
+
icon: ChevronRightIcon,
|
|
316
|
+
onClick: () => handleClick(),
|
|
317
|
+
disabled,
|
|
318
|
+
justify: "flex-start"
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
function MenuButton(props) {
|
|
322
|
+
const {
|
|
323
|
+
apiVersion = API_VERSION,
|
|
324
|
+
schemaType,
|
|
325
|
+
documentId,
|
|
326
|
+
languageField
|
|
327
|
+
} = props;
|
|
328
|
+
const client = useClient({
|
|
329
|
+
apiVersion
|
|
330
|
+
});
|
|
331
|
+
const supportedLanguages = Array.isArray(props.supportedLanguages) ? props.supportedLanguages :
|
|
332
|
+
// eslint-disable-next-line require-await
|
|
333
|
+
suspend(async () => {
|
|
334
|
+
if (typeof props.supportedLanguages === "function") {
|
|
335
|
+
return props.supportedLanguages(client);
|
|
336
|
+
}
|
|
337
|
+
return props.supportedLanguages;
|
|
338
|
+
}, []);
|
|
339
|
+
const [open, setOpen] = useState(false);
|
|
340
|
+
const handleClick = useCallback(() => setOpen(o => !o), []);
|
|
341
|
+
const [button, setButton] = useState(null);
|
|
342
|
+
const [popover, setPopover] = useState(null);
|
|
343
|
+
const handleClickOutside = useCallback(() => setOpen(false), []);
|
|
344
|
+
useClickOutside(handleClickOutside, [button, popover]);
|
|
345
|
+
const {
|
|
346
|
+
data: metadata,
|
|
347
|
+
loading,
|
|
348
|
+
error
|
|
349
|
+
} = useTranslationMetadata(documentId);
|
|
350
|
+
const {
|
|
351
|
+
draft,
|
|
352
|
+
published
|
|
353
|
+
} = useEditState(documentId, schemaType);
|
|
354
|
+
const source = draft || published;
|
|
355
|
+
const sourceLanguageId = source == null ? void 0 : source[languageField];
|
|
356
|
+
const sourceLanguageIsValid = supportedLanguages.some(l => l.id === sourceLanguageId);
|
|
357
|
+
const allLanguagesAreValid = React.useMemo(() => {
|
|
358
|
+
const valid = supportedLanguages.every(l => l.id && l.title);
|
|
359
|
+
if (!valid) {
|
|
360
|
+
console.warn("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.", supportedLanguages);
|
|
361
|
+
}
|
|
362
|
+
return valid;
|
|
363
|
+
}, [supportedLanguages]);
|
|
364
|
+
const content = /* @__PURE__ */jsx(Box, {
|
|
365
|
+
children: error ? /* @__PURE__ */jsx(Card, {
|
|
366
|
+
tone: "critical",
|
|
367
|
+
padding: 2,
|
|
368
|
+
children: /* @__PURE__ */jsxs(Text, {
|
|
369
|
+
children: ["Error: ", error]
|
|
370
|
+
})
|
|
371
|
+
}) : /* @__PURE__ */jsxs(Stack, {
|
|
372
|
+
padding: 1,
|
|
373
|
+
space: 1,
|
|
374
|
+
children: [supportedLanguages.length > 0 ? /* @__PURE__ */jsxs(Fragment, {
|
|
375
|
+
children: [supportedLanguages.map((language, langIndex) => {
|
|
376
|
+
var _a;
|
|
377
|
+
return !loading && sourceLanguageId && sourceLanguageIsValid ?
|
|
378
|
+
// Button to duplicate this document to a new translation
|
|
379
|
+
// And either create or update the metadata document
|
|
380
|
+
/* @__PURE__ */
|
|
381
|
+
jsx(LanguageOption, {
|
|
382
|
+
index: langIndex,
|
|
383
|
+
language,
|
|
384
|
+
languageField,
|
|
385
|
+
schemaType,
|
|
386
|
+
documentId,
|
|
387
|
+
disabled: loading || !allLanguagesAreValid,
|
|
388
|
+
current: language.id === sourceLanguageId,
|
|
389
|
+
metadata,
|
|
390
|
+
sourceId: documentId,
|
|
391
|
+
sourceLanguageId,
|
|
392
|
+
translation: metadata == null ? void 0 : metadata.translations.find(t => t._key === language.id)
|
|
393
|
+
}, language.id || language.title || "lang-".concat(langIndex)) :
|
|
394
|
+
// Button to set a language field on *this* document
|
|
395
|
+
/* @__PURE__ */
|
|
396
|
+
jsx(LanguagePatch, {
|
|
397
|
+
languageField,
|
|
398
|
+
source,
|
|
399
|
+
documentId,
|
|
400
|
+
schemaType,
|
|
401
|
+
language,
|
|
402
|
+
disabled: (_a = !allLanguagesAreValid || (metadata == null ? void 0 : metadata.translations.filter(t => {
|
|
403
|
+
var _a2;
|
|
404
|
+
return ((_a2 = t == null ? void 0 : t.value) == null ? void 0 : _a2._ref) !== documentId;
|
|
405
|
+
}).some(t => t._key === language.id))) != null ? _a : false
|
|
406
|
+
}, 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
|
+
})]
|
|
435
|
+
}) : null, /* @__PURE__ */jsx(LanguageManage, {
|
|
436
|
+
id: metadata == null ? void 0 : metadata._id
|
|
437
|
+
})]
|
|
438
|
+
})
|
|
439
|
+
});
|
|
440
|
+
return /* @__PURE__ */jsx(Popover, {
|
|
441
|
+
constrainSize: true,
|
|
442
|
+
content,
|
|
443
|
+
open,
|
|
444
|
+
portal: true,
|
|
445
|
+
ref: setPopover,
|
|
446
|
+
overflow: "auto",
|
|
447
|
+
children: /* @__PURE__ */jsx(Button, {
|
|
448
|
+
text: "Translations",
|
|
449
|
+
mode: "bleed",
|
|
450
|
+
disabled: !source,
|
|
451
|
+
tone: !source || !loading && sourceLanguageId && sourceLanguageIsValid ? void 0 : "caution",
|
|
452
|
+
icon: TranslateIcon,
|
|
453
|
+
onClick: handleClick,
|
|
454
|
+
ref: setButton,
|
|
455
|
+
selected: open
|
|
456
|
+
})
|
|
457
|
+
});
|
|
458
|
+
}
|
|
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) {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
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
|
+
};
|
|
472
|
+
}
|
|
473
|
+
const DEFAULT_CONFIG = {
|
|
474
|
+
supportedLanguages: [],
|
|
475
|
+
schemaTypes: [],
|
|
476
|
+
languageField: "language",
|
|
477
|
+
bulkPublish: false
|
|
478
|
+
};
|
|
479
|
+
const documentInternationalization = definePlugin(config => {
|
|
480
|
+
const {
|
|
481
|
+
supportedLanguages,
|
|
482
|
+
schemaTypes,
|
|
483
|
+
languageField,
|
|
484
|
+
bulkPublish
|
|
485
|
+
} = {
|
|
486
|
+
...DEFAULT_CONFIG,
|
|
487
|
+
...config
|
|
488
|
+
};
|
|
489
|
+
const renderLanguageFilter = (schemaType, documentId) => {
|
|
490
|
+
return /* @__PURE__ */jsx(MenuButton, {
|
|
491
|
+
supportedLanguages,
|
|
492
|
+
schemaType,
|
|
493
|
+
documentId: documentId != null ? documentId : "",
|
|
494
|
+
languageField
|
|
495
|
+
});
|
|
496
|
+
};
|
|
497
|
+
return {
|
|
498
|
+
name: "@sanity/document-internationalization",
|
|
499
|
+
// Adds:
|
|
500
|
+
// - A bulk-publishing UI component to the form
|
|
501
|
+
// - Will only work for projects on a compatible plan
|
|
502
|
+
form: {
|
|
503
|
+
components: {
|
|
504
|
+
input: props => {
|
|
505
|
+
if (bulkPublish && props.id === "root" && props.schemaType.name === METADATA_SCHEMA_NAME) {
|
|
506
|
+
return /* @__PURE__ */jsx(Stack, {
|
|
507
|
+
space: 5,
|
|
508
|
+
children: props.renderDefault(props)
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return props.renderDefault(props);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
// Adds:
|
|
516
|
+
// - The `Translations` dropdown to the editing form
|
|
517
|
+
// - `Badges` to documents with a language value
|
|
518
|
+
document: {
|
|
519
|
+
unstable_languageFilter: (prev, ctx) => {
|
|
520
|
+
const {
|
|
521
|
+
schemaType,
|
|
522
|
+
documentId
|
|
523
|
+
} = ctx;
|
|
524
|
+
return schemaTypes.includes(schemaType) ? [...prev, () => renderLanguageFilter(schemaType, documentId)] : prev;
|
|
525
|
+
},
|
|
526
|
+
badges: (prev, _ref2) => {
|
|
527
|
+
let {
|
|
528
|
+
schemaType
|
|
529
|
+
} = _ref2;
|
|
530
|
+
if (!schemaTypes.includes(schemaType)) {
|
|
531
|
+
return prev;
|
|
532
|
+
}
|
|
533
|
+
return [props => LanguageBadge(props, supportedLanguages, languageField), ...prev];
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
// Adds:
|
|
537
|
+
// - The `Translations metadata` document type to the schema
|
|
538
|
+
schema: {
|
|
539
|
+
// Create the metadata document type
|
|
540
|
+
types: [metadata(schemaTypes)],
|
|
541
|
+
// For every schema type this plugin is enabled on
|
|
542
|
+
// Create an initial value template to set the language
|
|
543
|
+
templates: (prev, _ref3) => {
|
|
544
|
+
let {
|
|
545
|
+
schema
|
|
546
|
+
} = _ref3;
|
|
547
|
+
if (!Array.isArray(supportedLanguages)) {
|
|
548
|
+
return prev;
|
|
549
|
+
}
|
|
550
|
+
const parameterizedTemplates = schemaTypes.map(schemaType => {
|
|
551
|
+
var _a, _b;
|
|
552
|
+
return {
|
|
553
|
+
id: "".concat(schemaType, "-parameterized"),
|
|
554
|
+
title: "".concat((_b = (_a = schema == null ? void 0 : schema.get(schemaType)) == null ? void 0 : _a.title) != null ? _b : schemaType, ": with Language"),
|
|
555
|
+
schemaType,
|
|
556
|
+
parameters: [{
|
|
557
|
+
name: "languageId",
|
|
558
|
+
title: "Language ID",
|
|
559
|
+
type: "string"
|
|
560
|
+
}],
|
|
561
|
+
value: _ref4 => {
|
|
562
|
+
let {
|
|
563
|
+
languageId
|
|
564
|
+
} = _ref4;
|
|
565
|
+
return {
|
|
566
|
+
[languageField]: languageId
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
});
|
|
571
|
+
const staticTemplates = schemaTypes.flatMap(schemaType => {
|
|
572
|
+
return supportedLanguages.map(language => {
|
|
573
|
+
var _a, _b;
|
|
574
|
+
return {
|
|
575
|
+
id: "".concat(schemaType, "-").concat(language.id),
|
|
576
|
+
title: "".concat(language.title, " ").concat((_b = (_a = schema == null ? void 0 : schema.get(schemaType)) == null ? void 0 : _a.title) != null ? _b : schemaType),
|
|
577
|
+
schemaType,
|
|
578
|
+
value: {
|
|
579
|
+
[languageField]: language.id
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
return [...prev, ...parameterizedTemplates, ...staticTemplates];
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
// Uses:
|
|
588
|
+
// - `sanity-plugin-internationalized-array` to maintain the translations array
|
|
589
|
+
plugins: [
|
|
590
|
+
// Translation metadata stores its references using this plugin
|
|
591
|
+
// It cuts down on attribute usage and gives UI conveniences to add new translations
|
|
592
|
+
internationalizedArray({
|
|
593
|
+
languages: supportedLanguages,
|
|
594
|
+
fieldTypes: [
|
|
595
|
+
// TODO: The plugin should allow this kind of input
|
|
596
|
+
// @ts-ignore
|
|
597
|
+
defineField({
|
|
598
|
+
name: "reference",
|
|
599
|
+
type: "reference",
|
|
600
|
+
to: schemaTypes.map(type => ({
|
|
601
|
+
type
|
|
602
|
+
})),
|
|
603
|
+
// TODO: Add a validation rule to *ensure* the document's language matches the array key
|
|
604
|
+
// Reference filters don't actually enforce validation!
|
|
605
|
+
// validation: (Rule) => Rule.custom(),
|
|
606
|
+
options: {
|
|
607
|
+
collapsed: false,
|
|
608
|
+
// TODO: Update type once it knows the values of this filter
|
|
609
|
+
// @ts-ignore
|
|
610
|
+
filter: _ref5 => {
|
|
611
|
+
let {
|
|
612
|
+
parent,
|
|
613
|
+
document
|
|
614
|
+
} = _ref5;
|
|
615
|
+
if (!parent) return null;
|
|
616
|
+
const parentArray = Array.isArray(parent) ? parent : [parent];
|
|
617
|
+
const language = parentArray.find(p => p._key);
|
|
618
|
+
if (!(language == null ? void 0 : language._key)) return null;
|
|
619
|
+
if (document.schemaTypes) {
|
|
620
|
+
return {
|
|
621
|
+
filter: "_type in $schemaTypes && ".concat(languageField, " == $language"),
|
|
622
|
+
params: {
|
|
623
|
+
schemaTypes: document.schemaTypes,
|
|
624
|
+
language: language._key
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
return {
|
|
629
|
+
filter: "".concat(languageField, " == $language"),
|
|
630
|
+
params: {
|
|
631
|
+
language: language._key
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}, {
|
|
637
|
+
strict: false
|
|
638
|
+
})]
|
|
639
|
+
})]
|
|
640
|
+
};
|
|
641
|
+
});
|
|
642
|
+
export { documentInternationalization };
|
|
643
|
+
//# sourceMappingURL=index.esm.js.map
|