sanity-plugin-internationalized-array 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  > This is the **Sanity Studio v3 version** of sanity-plugin-internationalized-array.
2
2
  >
3
- > For the v2 version, please refer to the [v2-branch](https://github.com/SimeonGriggs/sanity-plugin-internationalized-array/tree/studio-v2).
3
+ > For the v2 version, please refer to the [v2-branch](https://github.com/sanity-io/sanity-plugin-internationalized-array/tree/studio-v2).
4
4
 
5
5
  # sanity-plugin-internationalized-array
6
6
 
7
7
  A helper function that renders a custom input component for writing localized fields of content into an array.
8
8
 
9
- ![Screenshot of an internationalized input](./img/screenshot.png)
9
+ ![Screenshot of an internationalized input](./img/internationalized-array.png)
10
10
 
11
11
  ## Installation
12
12
 
@@ -36,6 +36,7 @@ import {internationalizedArray} from 'sanity-plugin-internationalized-array'
36
36
  {id: 'en', title: 'English'},
37
37
  {id: 'fr', title: 'French'}
38
38
  ],
39
+ defaultLanguages: ['en'],
39
40
  fieldTypes: ['string'],
40
41
  })
41
42
  ]
@@ -47,6 +48,8 @@ This will register two new fields to the schema, based on the settings passed in
47
48
  - `internationalizedArrayString` an array field of:
48
49
  - `internationalizedArrayStringValue` an object field, with a single `string` field inside called `value`
49
50
 
51
+ The above config will also create an empty array item in new documents for each language in `defaultLanguages`. This is configured globally for all internationalized array fields.
52
+
50
53
  You can pass in more registered schema-type names to generate more internationalized arrays. Use them in your schema like this:
51
54
 
52
55
  ```ts
@@ -138,6 +141,109 @@ This would also create two new fields in your schema.
138
141
 
139
142
  Note that the `name` key in the field gets rewritten to `value` and is instead used to name the object field.
140
143
 
144
+ ## Creating internationalized objects
145
+
146
+ Due to how fields are created, you cannot use anonymous objects in the `fieldTypes` array. You must register the object type in your Studio's schema as an "alias type".
147
+
148
+ ```ts
149
+ // ./schemas/seoType.ts
150
+
151
+ import {defineField} from 'sanity'
152
+
153
+ export const seoType = defineField({
154
+ name: 'seo',
155
+ title: 'SEO',
156
+ type: 'object',
157
+ fields: [
158
+ defineField({name: 'title', type: 'string'}),
159
+ defineField({name: 'description', type: 'string'}),
160
+ ],
161
+ })
162
+ ```
163
+
164
+ Then in your plugin configuration settings, add the name of your alias type to the `fieldTypes` setting.
165
+
166
+ ```ts
167
+ internationalizedArray({
168
+ languages: [
169
+ //...languages
170
+ ],
171
+ fieldTypes: ['seo'],
172
+ })
173
+ ```
174
+
175
+ Lastly, add the field to your schema.
176
+
177
+ ```ts
178
+ // ./schemas/post.ts
179
+
180
+ import {defineField, defineType} from 'sanity'
181
+
182
+ export default defineType({
183
+ name: 'post',
184
+ title: 'Post',
185
+ type: 'document',
186
+ fields: [
187
+ defineField({
188
+ name: 'seo',
189
+ type: 'internationalizedArraySeo',
190
+ }),
191
+ ],
192
+ })
193
+ ```
194
+
195
+ ## Usage with @sanity/language-filter
196
+
197
+ If you have many languages and authors that predominately write in only a few, [@sanity/language-filter](https://github.com/sanity-io/language-filter) can be used to reduce the number of language fields shown in the document form.
198
+
199
+ ![Internationalized array field filtered with language-filter](https://github.com/sanity-io/language-filter/assets/9684022/4b402520-4128-4e6e-af07-960a10be397e)
200
+
201
+ Configure both plugins in your sanity.config.ts file:
202
+
203
+ ```ts
204
+ // ./sanity.config.ts
205
+
206
+ import {languageFilter} from '@sanity/language-filter'
207
+
208
+ export default defineConfig({
209
+ // ... other config
210
+ plugins: [
211
+ // ... other plugins
212
+ languageFilter({
213
+ // Use the same languages as the internationalized array plugin
214
+ supportedLanguages: SUPPORTED_LANGUAGES,
215
+ defaultLanguages: [],
216
+ documentTypes: ['post'],
217
+ filterField: (enclosingType, member, selectedLanguageIds) => {
218
+ // Filter internationalized arrays
219
+ if (
220
+ enclosingType.jsonType === 'object' &&
221
+ enclosingType.name.startsWith('internationalizedArray') &&
222
+ 'kind' in member
223
+ ) {
224
+ const language = isKeyedObject(member.field.path[1])
225
+ ? member.field.path[1]._key
226
+ : null
227
+
228
+ return language ? selectedLanguageIds.includes(language) : false
229
+ }
230
+
231
+ // Filter internationalized objects if you have them
232
+ // `localeString` must be registered as a custom schema type
233
+ if (
234
+ enclosingType.jsonType === 'object' &&
235
+ enclosingType.name.startsWith('locale')
236
+ ) {
237
+ return selectedLanguageIds.includes(member.name)
238
+ }
239
+
240
+ return true
241
+ },
242
+ }),
243
+ ],
244
+ })
245
+ ```
246
+
141
247
  ## Shape of stored data
142
248
 
143
249
  The custom input contains buttons which will add new array items with the language as the `_key` value. Data returned from this array will look like this:
@@ -161,7 +267,7 @@ Using GROQ filters you can query for a specific language key like so:
161
267
 
162
268
  ## Migrate from objects to arrays
163
269
 
164
- [See the migration script](https://github.com/SimeonGriggs/sanity-plugin-internationalized-array/blob/main/migrations/transformObjectToArray.js) inside `./migrations/transformObjectToArray.js` of this Repo.
270
+ [See the migration script](https://github.com/sanity-io/sanity-plugin-internationalized-array/blob/main/migrations/transformObjectToArray.js) inside `./migrations/transformObjectToArray.js` of this Repo.
165
271
 
166
272
  Follow the instructions inside the script and set the `_type` and field name you wish to target.
167
273
 
@@ -222,7 +328,7 @@ on how to run this plugin with hotreload in the studio.
222
328
 
223
329
  ### Release new version
224
330
 
225
- Run ["CI & Release" workflow](https://github.com/SimeonGriggs/sanity-plugin-internationalized-array/actions/workflows/main.yml).
331
+ Run ["CI & Release" workflow](https://github.com/sanity-io/sanity-plugin-internationalized-array/actions/workflows/main.yml).
226
332
  Make sure to select the main branch and check "Release new version".
227
333
 
228
334
  Semantic release will only release on configured branches, so it is safe to run release on any branch.
package/lib/index.d.ts CHANGED
@@ -34,6 +34,7 @@ export declare type ArraySchemaWithLanguageOptions = ArraySchemaType & {
34
34
  select?: Record<string, string>
35
35
  languages: Language[] | LanguageCallback
36
36
  apiVersion: string
37
+ defaultLanguages?: string[]
37
38
  }
38
39
  }
39
40
 
@@ -98,6 +99,15 @@ export declare type PluginConfig = {
98
99
  * ```
99
100
  */
100
101
  languages: Language[] | LanguageCallback
102
+ /**
103
+ * You can specify a list of language IDs that should be pre-filled when creating a new document
104
+ * ```tsx
105
+ * {
106
+ * defaultLanguages: ['en']
107
+ * }
108
+ * ```
109
+ */
110
+ defaultLanguages?: string[]
101
111
  /**
102
112
  * Can be a string matching core field types, as well as custom ones:
103
113
  * ```tsx
package/lib/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import*as suspend from'suspend-react';import{suspend as suspend$1}from'suspend-react';import{jsx,jsxs,Fragment}from'react/jsx-runtime';import{useClient,useFormBuilder,insert,setIfMissing,set,ArrayOfObjectsItem,defineField,useFormValue,unset,definePlugin}from'sanity';import React,{memo,useDeferredValue,useMemo,useCallback,useEffect}from'react';import{AddIcon,RemoveCircleIcon}from'@sanity/icons';import{Card,Stack,Text,Code,useToast,Grid,Button,Spinner,Label,MenuButton,Menu,MenuItem,Flex}from'@sanity/ui';import equal from'fast-deep-equal';const namespace="sanity-plugin-internationalized-array";const version="v0";const preload=fn=>suspend.preload(()=>fn(),[version,namespace]);const clear=()=>suspend.clear([version,namespace]);const peek=selectedValue=>suspend.peek([version,namespace,selectedValue]);var Preload=memo(function Preload(props){const client=useClient({apiVersion:props.apiVersion});if(!Array.isArray(peek({}))){preload(async()=>Array.isArray(props.languages)?props.languages:props.languages(client,{}));}return null;});function camelCase(string){return string.replace(/-([a-z])/g,g=>g[1].toUpperCase());}function titleCase(string){return string.split(" ").map(word=>word.charAt(0).toUpperCase()+word.slice(1)).join(" ");}function pascalCase(string){return titleCase(camelCase(string));}function createFieldName(name){let addValue=arguments.length>1&&arguments[1]!==undefined?arguments[1]:false;return addValue?["internationalizedArray",pascalCase(name),"Value"].join(""):["internationalizedArray",pascalCase(name)].join("");}var commonjsGlobal=typeof globalThis!=='undefined'?globalThis:typeof window!=='undefined'?window:typeof global!=='undefined'?global:typeof self!=='undefined'?self:{};var lodash={exports:{}};/**
1
+ import*as suspend from'suspend-react';import{suspend as suspend$1}from'suspend-react';import{jsx,jsxs,Fragment}from'react/jsx-runtime';import{useClient,useFormBuilder,useFormValue,insert,setIfMissing,set,ArrayOfObjectsItem,defineField,unset,definePlugin}from'sanity';import React,{memo,useDeferredValue,useMemo,useCallback,useEffect,useContext}from'react';import{AddIcon,RemoveCircleIcon}from'@sanity/icons';import{useLanguageFilterStudioContext}from'@sanity/language-filter';import{Card,Stack,Text,Code,useToast,Grid,Button,Spinner,Label,MenuButton,Menu,MenuItem,Flex}from'@sanity/ui';import equal from'fast-deep-equal';const namespace="sanity-plugin-internationalized-array";const version="v0";const preload=fn=>suspend.preload(()=>fn(),[version,namespace]);const clear=()=>suspend.clear([version,namespace]);const peek=selectedValue=>suspend.peek([version,namespace,selectedValue]);var Preload=memo(function Preload(props){const client=useClient({apiVersion:props.apiVersion});if(!Array.isArray(peek({}))){preload(async()=>Array.isArray(props.languages)?props.languages:props.languages(client,{}));}return null;});function camelCase(string){return string.replace(/-([a-z])/g,g=>g[1].toUpperCase());}function titleCase(string){return string.split(" ").map(word=>word.charAt(0).toUpperCase()+word.slice(1)).join(" ");}function pascalCase(string){return titleCase(camelCase(string));}function createFieldName(name){let addValue=arguments.length>1&&arguments[1]!==undefined?arguments[1]:false;return addValue?["internationalizedArray",pascalCase(name),"Value"].join(""):["internationalizedArray",pascalCase(name)].join("");}var commonjsGlobal=typeof globalThis!=='undefined'?globalThis:typeof window!=='undefined'?window:typeof global!=='undefined'?global:typeof self!=='undefined'?self:{};var lodash={exports:{}};/**
2
2
  * @license
3
3
  * Lodash <https://lodash.com/>
4
4
  * Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
@@ -9467,24 +9467,27 @@ var _=runInContext();// Some AMD build optimizers, like r.js, check for conditio
9467
9467
  if(freeModule){// Export for Node.js.
9468
9468
  (freeModule.exports=_)._=_;// Export for CommonJS support.
9469
9469
  freeExports._=_;}else{// Export to the global object.
9470
- root._=_;}}).call(commonjsGlobal);})(lodash,lodash.exports);var lodashExports=lodash.exports;const getSelectedValue=(select,document)=>{if(!select||!document){return{};}const selection=select||{};const selectedValue={};for(const[key,path]of Object.entries(selection)){let value=lodashExports.get(document,path);if(Array.isArray(value)){value=value.filter(item=>typeof item==="object"?(item==null?void 0:item._type)==="reference"&&"_ref"in item:true);}selectedValue[key]=value;}return selectedValue;};const schemaExample={languages:[{id:"en",title:"English"},{id:"no",title:"Norsk"}]};function Feedback(){return/* @__PURE__ */jsx(Card,{tone:"caution",border:true,radius:2,padding:3,children:/* @__PURE__ */jsxs(Stack,{space:4,children:[/* @__PURE__ */jsxs(Text,{children:["An array of language objects must be passed into the ",/* @__PURE__ */jsx("code",{children:"internationalizedArray"})," ","helper function, each with an ",/* @__PURE__ */jsx("code",{children:"id"})," and ",/* @__PURE__ */jsx("code",{children:"title"})," field. Example:"]}),/* @__PURE__ */jsx(Card,{padding:2,border:true,radius:2,children:/* @__PURE__ */jsx(Code,{size:1,language:"javascript",children:JSON.stringify(schemaExample,null,2)})})]})});}const LanguageContext=React.createContext({languages:[]});const LanguageProvider=LanguageContext.Provider;function InternationalizedArray(props){const{members,value,schemaType,onChange}=props;const readOnly=typeof schemaType.readOnly==="boolean"?schemaType.readOnly:false;const{options}=schemaType;const toast=useToast();const{value:document}=useFormBuilder();const deferredDocument=useDeferredValue(document);const selectedValue=useMemo(()=>getSelectedValue(options.select,deferredDocument),[options.select,deferredDocument]);const{apiVersion}=options;const client=useClient({apiVersion});const languages=Array.isArray(options.languages)?options.languages:suspend$1(// eslint-disable-next-line require-await
9471
- async()=>{if(typeof options.languages==="function"){return options.languages(client,selectedValue);}return options.languages;},[version,namespace,selectedValue],{equal});const handleAddLanguage=useCallback(languageId=>{if(!(languages==null?void 0:languages.length)){return;}const itemBase={_type:"".concat(schemaType.name,"Value")};const newItems=languageId?// Just one for this language
9472
- [{...itemBase,_key:languageId}]:// Or one for every missing language
9473
- languages.filter(language=>(value==null?void 0:value.length)?!value.find(v=>v._key===language.id):true).map(language=>({...itemBase,_key:language.id}));const languagesInUse2=(value==null?void 0:value.length)?value.map(v=>v):[];const insertions=newItems.map(item=>{const languageIndex=languages.findIndex(l=>item._key===l.id);const remainingLanguages=languages.slice(languageIndex+1);const nextLanguageIndex=languagesInUse2.findIndex(l=>// eslint-disable-next-line max-nested-callbacks
9470
+ root._=_;}}).call(commonjsGlobal);})(lodash,lodash.exports);var lodashExports=lodash.exports;const getSelectedValue=(select,document)=>{if(!select||!document){return{};}const selection=select||{};const selectedValue={};for(const[key,path]of Object.entries(selection)){let value=lodashExports.get(document,path);if(Array.isArray(value)){value=value.filter(item=>typeof item==="object"?(item==null?void 0:item._type)==="reference"&&"_ref"in item:true);}selectedValue[key]=value;}return selectedValue;};const MAX_COLUMNS=7;const schemaExample={languages:[{id:"en",title:"English"},{id:"no",title:"Norsk"}]};function Feedback(){return/* @__PURE__ */jsx(Card,{tone:"caution",border:true,radius:2,padding:3,children:/* @__PURE__ */jsxs(Stack,{space:4,children:[/* @__PURE__ */jsxs(Text,{children:["An array of language objects must be passed into the"," ",/* @__PURE__ */jsx("code",{children:"internationalizedArray"})," helper function, each with an"," ",/* @__PURE__ */jsx("code",{children:"id"})," and ",/* @__PURE__ */jsx("code",{children:"title"})," field. Example:"]}),/* @__PURE__ */jsx(Card,{padding:2,border:true,radius:2,children:/* @__PURE__ */jsx(Code,{size:1,language:"javascript",children:JSON.stringify(schemaExample,null,2)})})]})});}const LanguageContext=React.createContext({languages:[]});const LanguageProvider=LanguageContext.Provider;function InternationalizedArray(props){const{members,value,schemaType,onChange}=props;const readOnly=typeof schemaType.readOnly==="boolean"?schemaType.readOnly:false;const{options}=schemaType;const toast=useToast();const{value:document}=useFormBuilder();const deferredDocument=useDeferredValue(document);const selectedValue=useMemo(()=>getSelectedValue(options.select,deferredDocument),[options.select,deferredDocument]);const{apiVersion,defaultLanguages}=options;const client=useClient({apiVersion});const languages=Array.isArray(options.languages)?options.languages:suspend$1(// eslint-disable-next-line require-await
9471
+ async()=>{if(typeof options.languages==="function"){return options.languages(client,selectedValue);}return options.languages;},[version,namespace,selectedValue],{equal});const{selectedLanguageIds,options:languageFilterOptions}=useLanguageFilterStudioContext();const documentType=useFormValue(["_type"]);const languageFilterEnabled=typeof documentType==="string"&&languageFilterOptions.documentTypes.includes(documentType);const filteredMembers=useMemo(()=>languageFilterEnabled?members.filter(member=>{if(member.kind!=="item"){return false;}const valueMember=member.item.members[0];if(valueMember.kind!=="field"){return false;}return languageFilterOptions.filterField(member.item.schemaType,valueMember,selectedLanguageIds);}):members,[languageFilterEnabled,members,languageFilterOptions,selectedLanguageIds]);const filteredLanguages=useMemo(()=>languageFilterEnabled?languages.filter(language=>selectedLanguageIds.includes(language.id)):languages,[languageFilterEnabled,languages,selectedLanguageIds]);const handleAddLanguage=useCallback(param=>{var _a;if(!(filteredLanguages==null?void 0:filteredLanguages.length)){return;}const languageIds=Array.isArray(param)?param:[(_a=param==null?void 0:param.currentTarget)==null?void 0:_a.value].filter(Boolean);const itemBase={_type:"".concat(schemaType.name,"Value")};const newItems=Array.isArray(languageIds)&&languageIds.length>0?// Just one for this language
9472
+ languageIds.map(id=>({...itemBase,_key:id})):// Or one for every missing language
9473
+ filteredLanguages.filter(language=>(value==null?void 0:value.length)?!value.find(v=>v._key===language.id):true).map(language=>({...itemBase,_key:language.id}));const languagesInUse2=(value==null?void 0:value.length)?value.map(v=>v):[];const insertions=newItems.map(item=>{const languageIndex=languages.findIndex(l=>item._key===l.id);const remainingLanguages=languages.slice(languageIndex+1);const nextLanguageIndex=languagesInUse2.findIndex(l=>// eslint-disable-next-line max-nested-callbacks
9474
9474
  remainingLanguages.find(r=>r.id===l._key));if(nextLanguageIndex<0){languagesInUse2.push(item);}else{languagesInUse2.splice(nextLanguageIndex,0,item);}return nextLanguageIndex<0?// No next language (-1), add to end of array
9475
9475
  insert([item],"after",[nextLanguageIndex]):// Next language found, insert before that
9476
- insert([item],"before",[nextLanguageIndex]);});onChange([setIfMissing([]),...insertions]);},[languages,onChange,schemaType.name,value]);const handleRestoreOrder=useCallback(()=>{if(!(value==null?void 0:value.length)||!(languages==null?void 0:languages.length)){return;}const updatedValue=value.reduce((acc,v)=>{const newIndex=languages.findIndex(l=>l.id===(v==null?void 0:v._key));if(newIndex>-1){acc[newIndex]=v;}return acc;},[]).filter(Boolean);if((value==null?void 0:value.length)!==updatedValue.length){toast.push({title:"There was an error reordering languages",status:"warning"});}onChange(set(updatedValue));},[toast,languages,onChange,value]);const allKeysAreLanguages=useMemo(()=>{if(!(value==null?void 0:value.length)||!(languages==null?void 0:languages.length)){return true;}return value==null?void 0:value.every(v=>languages.find(l=>(l==null?void 0:l.id)===(v==null?void 0:v._key)));},[value,languages]);const languagesInUse=useMemo(()=>languages&&languages.length>1?languages.filter(l=>value==null?void 0:value.find(v=>v._key===l.id)):[],[languages,value]);const languagesOutOfOrder=useMemo(()=>{if(!(value==null?void 0:value.length)||!languagesInUse.length){return[];}return value.map((v,vIndex)=>vIndex===languagesInUse.findIndex(l=>l.id===v._key)?null:v).filter(Boolean);},[value,languagesInUse]);const languagesAreValid=useMemo(()=>!(languages==null?void 0:languages.length)||(languages==null?void 0:languages.length)&&languages.every(item=>item.id&&item.title),[languages]);useEffect(()=>{if(languagesOutOfOrder.length>0&&allKeysAreLanguages){handleRestoreOrder();}},[languagesOutOfOrder,allKeysAreLanguages,handleRestoreOrder]);if(!languagesAreValid){return/* @__PURE__ */jsx(Feedback,{});}return/* @__PURE__ */jsx(LanguageProvider,{value:{languages},children:/* @__PURE__ */jsxs(Stack,{space:2,children:[(members==null?void 0:members.length)>0?/* @__PURE__ */jsx(Fragment,{children:members.map(member=>{if(member.kind==="item"){return/* @__PURE__ */jsx(ArrayOfObjectsItem,{member,renderItem:props.renderItem,renderField:props.renderField,renderInput:props.renderInput,renderPreview:props.renderPreview},member.key);}return null;})}):null,(languages==null?void 0:languages.length)>0&&languagesInUse.length<languages.length?/* @__PURE__ */jsxs(Stack,{space:2,children:[languages.length>1?/* @__PURE__ */jsx(Grid,{columns:Math.min(languages.length,5),gap:2,children:languages.map(language=>/* @__PURE__ */jsx(Button,{tone:"primary",mode:"ghost",fontSize:1,disabled:readOnly||Boolean(value==null?void 0:value.find(item=>item._key===language.id)),text:language.id.toUpperCase(),icon:AddIcon,onClick:()=>handleAddLanguage(language.id)},language.id))}):null,/* @__PURE__ */jsx(Button,{tone:"primary",mode:"ghost",disabled:readOnly||value&&(value==null?void 0:value.length)>=(languages==null?void 0:languages.length),icon:AddIcon,text:// eslint-disable-next-line no-nested-ternary
9477
- (value==null?void 0:value.length)?"Add missing ".concat(languages.length-value.length===1?"language":"languages"):languages.length===1?"Add ".concat(languages[0].title," Field"):"Add all languages",onClick:()=>handleAddLanguage()})]}):null]})});}var array=config=>{const{apiVersion,select,languages,type}=config;const typeName=typeof type==="string"?type:type.name;const arrayName=createFieldName(typeName);const objectName=createFieldName(typeName,true);return defineField({name:arrayName,title:"Internationalized array",type:"array",components:{input:InternationalizedArray},options:{apiVersion,select,languages},// TODO: Resolve this typing issue with the inner object
9476
+ insert([item],"before",[nextLanguageIndex]);});onChange([setIfMissing([]),...insertions]);},[filteredLanguages,onChange,schemaType.name,value]);const documentCreatedAt=useFormValue(["_createdAt"]);if(// Array field is empty
9477
+ !value&&// Document form is in "not yet created" state
9478
+ !documentCreatedAt&&// Plugin config included default languages
9479
+ defaultLanguages&&(defaultLanguages==null?void 0:defaultLanguages.length)>0){handleAddLanguage(defaultLanguages);}const handleRestoreOrder=useCallback(()=>{if(!(value==null?void 0:value.length)||!(languages==null?void 0:languages.length)){return;}const updatedValue=value.reduce((acc,v)=>{const newIndex=languages.findIndex(l=>l.id===(v==null?void 0:v._key));if(newIndex>-1){acc[newIndex]=v;}return acc;},[]).filter(Boolean);if((value==null?void 0:value.length)!==updatedValue.length){toast.push({title:"There was an error reordering languages",status:"warning"});}onChange(set(updatedValue));},[toast,languages,onChange,value]);const allKeysAreLanguages=useMemo(()=>{if(!(value==null?void 0:value.length)||!(languages==null?void 0:languages.length)){return true;}return value==null?void 0:value.every(v=>languages.find(l=>(l==null?void 0:l.id)===(v==null?void 0:v._key)));},[value,languages]);const languagesInUse=useMemo(()=>languages&&languages.length>1?languages.filter(l=>value==null?void 0:value.find(v=>v._key===l.id)):[],[languages,value]);const languagesOutOfOrder=useMemo(()=>{if(!(value==null?void 0:value.length)||!languagesInUse.length){return[];}return value.map((v,vIndex)=>vIndex===languagesInUse.findIndex(l=>l.id===v._key)?null:v).filter(Boolean);},[value,languagesInUse]);const languagesAreValid=useMemo(()=>!(languages==null?void 0:languages.length)||(languages==null?void 0:languages.length)&&languages.every(item=>item.id&&item.title),[languages]);useEffect(()=>{if(languagesOutOfOrder.length>0&&allKeysAreLanguages){handleRestoreOrder();}},[languagesOutOfOrder,allKeysAreLanguages,handleRestoreOrder]);const allLanguagesArePresent=useMemo(()=>{const filteredLanguageIds=filteredLanguages.map(l=>l.id);const languagesInUseIds=value?value.map(v=>v._key):[];return languagesInUseIds.length===filteredLanguageIds.length&&languagesInUseIds.every(l=>filteredLanguageIds.includes(l));},[filteredLanguages,value]);if(!languagesAreValid){return/* @__PURE__ */jsx(Feedback,{});}return/* @__PURE__ */jsx(LanguageProvider,{value:{languages:filteredLanguages},children:/* @__PURE__ */jsxs(Stack,{space:2,children:[(members==null?void 0:members.length)>0?/* @__PURE__ */jsx(Fragment,{children:filteredMembers.map(member=>{if(member.kind==="item"){return/* @__PURE__ */jsx(ArrayOfObjectsItem,{member,renderItem:props.renderItem,renderField:props.renderField,renderInput:props.renderInput,renderPreview:props.renderPreview},member.key);}return null;})}):null,(filteredLanguages==null?void 0:filteredLanguages.length)>0&&!allLanguagesArePresent?/* @__PURE__ */jsxs(Stack,{space:2,children:[filteredLanguages.length>1?/* @__PURE__ */jsx(Grid,{columns:Math.min(filteredLanguages.length,MAX_COLUMNS),gap:2,children:filteredLanguages.map(language=>/* @__PURE__ */jsx(Button,{tone:"primary",mode:"ghost",fontSize:1,disabled:readOnly||Boolean(value==null?void 0:value.find(item=>item._key===language.id)),text:language.id.toUpperCase(),icon:filteredLanguages.length>MAX_COLUMNS?void 0:AddIcon,value:language.id,onClick:handleAddLanguage},language.id))}):null,/* @__PURE__ */jsx(Button,{tone:"primary",mode:"ghost",disabled:readOnly||allLanguagesArePresent,icon:AddIcon,text:// eslint-disable-next-line no-nested-ternary
9480
+ (value==null?void 0:value.length)?"Add missing ".concat(filteredLanguages.length-value.length===1?"language":"languages"):filteredLanguages.length===1?"Add ".concat(filteredLanguages[0].title," Field"):"Add all languages",onClick:handleAddLanguage})]}):null]})});}var array=config=>{const{apiVersion,select,languages,defaultLanguages,type}=config;const typeName=typeof type==="string"?type:type.name;const arrayName=createFieldName(typeName);const objectName=createFieldName(typeName,true);return defineField({name:arrayName,title:"Internationalized array",type:"array",components:{input:InternationalizedArray},options:{apiVersion,select,languages,defaultLanguages},// TODO: Resolve this typing issue with the inner object
9478
9481
  // @ts-expect-error
9479
9482
  of:[defineField({...(typeof type==="string"?{}:type),name:objectName,type:objectName})],validation:rule=>rule.custom(async(value,context)=>{var _a,_b,_c;if(!value){return true;}const selectedValue=getSelectedValue(select,context.document);const client=context.getClient({apiVersion});const contextLanguages=Array.isArray((_b=(_a=context==null?void 0:context.type)==null?void 0:_a.options)==null?void 0:_b.languages)?context.type.options.languages:Array.isArray(peek(selectedValue))?peek(selectedValue):await((_c=context==null?void 0:context.type)==null?void 0:_c.options.languages(client,selectedValue));if(value&&value.length>contextLanguages.length){return"Cannot be more than ".concat(contextLanguages.length===1?"1 item":"".concat(contextLanguages.length," items"));}const nonLanguageKeys=(value==null?void 0:value.length)?value.filter(item=>!contextLanguages.find(language=>item._key===language.id)):[];if(nonLanguageKeys.length){return{message:"Array item keys must be valid languages registered to the field type",paths:nonLanguageKeys.map(item=>[{_key:item._key}])};}const valuesByLanguage=(value==null?void 0:value.length)?value.filter(item=>Boolean(item==null?void 0:item._key)).reduce((acc,cur)=>{if(acc[cur._key]){return{...acc,[cur._key]:[...acc[cur._key],cur]};}return{...acc,[cur._key]:[cur]};},{}):{};const duplicateValues=Object.values(valuesByLanguage).filter(item=>(item==null?void 0:item.length)>1).flat();if(duplicateValues.length){return{message:"There can only be one field per language",paths:duplicateValues.map(item=>[{_key:item._key}])};}return true;})});};function InternationalizedField(props){if(props.schemaType.name==="reference"&&props.value){return props.renderDefault({...props,title:"",level:0});}return props.children;}function getToneFromValidation(validations){if(!(validations==null?void 0:validations.length)){return void 0;}const validationLevels=validations.map(v=>v.level);if(validationLevels.includes("error")){return"critical";}else if(validationLevels.includes("warning")){return"caution";}return void 0;}function InternationalizedInput(props){const parentValue=useFormValue(props.path.slice(0,-1));const inlineProps={...props.inputProps,// This is the magic that makes inline editing work?
9480
9483
  members:props.inputProps.members.filter(m=>m.kind==="field"&&m.name==="value"),// This just overrides the type
9481
9484
  // TODO: Remove this as it shouldn't be necessary?
9482
- value:props.value};const{validation,value,onChange,readOnly}=inlineProps;const{languages}=React.useContext(LanguageContext);const languageKeysInUse=useMemo(()=>{var _a;return(_a=parentValue==null?void 0:parentValue.map(v=>v._key))!=null?_a:[];},[parentValue]);const keyIsValid=(languages==null?void 0:languages.length)?languages.find(l=>l.id===value._key):false;const handleKeyChange=useCallback(languageId=>{if(!value||!(languages==null?void 0:languages.length)||!languages.find(l=>l.id===languageId)){return;}onChange([set(languageId,["_key"])]);},[onChange,value,languages]);const handleUnset=useCallback(()=>{onChange(unset());},[onChange]);if(!languages){return/* @__PURE__ */jsx(Spinner,{});}return/* @__PURE__ */jsx(Card,{paddingTop:2,tone:getToneFromValidation(validation),children:/* @__PURE__ */jsxs(Stack,{space:2,children:[/* @__PURE__ */jsx(Card,{tone:"inherit",children:keyIsValid?/* @__PURE__ */jsx(Label,{muted:true,size:1,children:value._key}):/* @__PURE__ */jsx(MenuButton,{button:/* @__PURE__ */jsx(Button,{fontSize:1,text:"Change \"".concat(value._key,"\"")}),id:"".concat(value._key,"-change-key"),menu:/* @__PURE__ */jsx(Menu,{children:languages.map(language=>/* @__PURE__ */jsx(MenuItem,{disabled:languageKeysInUse.includes(language.id),fontSize:1,text:language.id.toLocaleUpperCase(),onClick:()=>handleKeyChange(language.id)},language.id))}),placement:"right",popover:{portal:true}})}),/* @__PURE__ */jsxs(Flex,{align:"center",gap:2,children:[/* @__PURE__ */jsx(Card,{flex:1,tone:"inherit",children:props.inputProps.renderInput(props.inputProps)}),/* @__PURE__ */jsx(Card,{tone:"inherit",children:/* @__PURE__ */jsx(Button,{mode:"bleed",icon:RemoveCircleIcon,tone:"critical",disabled:readOnly,onClick:handleUnset})})]})]})});}var object=config=>{const{type}=config;const typeName=typeof type==="string"?type:type.name;const objectName=createFieldName(typeName,true);return defineField({name:objectName,title:"Internationalized array ".concat(type),type:"object",components:{item:InternationalizedInput},// TODO: Address this typing issue with the inner object
9485
+ value:props.value};const{validation,value,onChange,readOnly}=inlineProps;const{languages}=useContext(LanguageContext);const languageKeysInUse=useMemo(()=>{var _a;return(_a=parentValue==null?void 0:parentValue.map(v=>v._key))!=null?_a:[];},[parentValue]);const keyIsValid=(languages==null?void 0:languages.length)?languages.find(l=>l.id===value._key):false;const handleKeyChange=useCallback(event=>{var _a;const languageId=(_a=event==null?void 0:event.currentTarget)==null?void 0:_a.value;if(!value||!(languages==null?void 0:languages.length)||!languages.find(l=>l.id===languageId)){return;}onChange([set(languageId,["_key"])]);},[onChange,value,languages]);const handleUnset=useCallback(()=>{onChange(unset());},[onChange]);if(!languages){return/* @__PURE__ */jsx(Spinner,{});}return/* @__PURE__ */jsx(Card,{paddingTop:2,tone:getToneFromValidation(validation),children:/* @__PURE__ */jsxs(Stack,{space:2,children:[/* @__PURE__ */jsx(Card,{tone:"inherit",children:keyIsValid?/* @__PURE__ */jsx(Label,{muted:true,size:1,children:value._key}):/* @__PURE__ */jsx(MenuButton,{button:/* @__PURE__ */jsx(Button,{fontSize:1,text:"Change \"".concat(value._key,"\"")}),id:"".concat(value._key,"-change-key"),menu:/* @__PURE__ */jsx(Menu,{children:languages.map(language=>/* @__PURE__ */jsx(MenuItem,{disabled:languageKeysInUse.includes(language.id),fontSize:1,text:language.id.toLocaleUpperCase(),value:language.id,onClick:handleKeyChange},language.id))}),popover:{portal:true}})}),/* @__PURE__ */jsxs(Flex,{align:"center",gap:2,children:[/* @__PURE__ */jsx(Card,{flex:1,tone:"inherit",children:props.inputProps.renderInput(props.inputProps)}),/* @__PURE__ */jsx(Card,{tone:"inherit",children:/* @__PURE__ */jsx(Button,{mode:"bleed",icon:RemoveCircleIcon,tone:"critical",disabled:readOnly,onClick:handleUnset})})]})]})});}var object=config=>{const{type}=config;const typeName=typeof type==="string"?type:type.name;const objectName=createFieldName(typeName,true);return defineField({name:objectName,title:"Internationalized array ".concat(type),type:"object",components:{item:InternationalizedInput},// TODO: Address this typing issue with the inner object
9483
9486
  // @ts-expect-error
9484
9487
  fields:[typeof type==="string"?// Define a simple field if all we have is the name as a string
9485
9488
  defineField({name:"value",type,components:{// TODO: Address this typing issue with the inner object
9486
9489
  // @ts-expect-error
9487
9490
  field:InternationalizedField}}):// Pass in the configured options, but overwrite the name
9488
- {...type,name:"value",components:{field:InternationalizedField}}],preview:{select:{title:"value",subtitle:"_key"}}});};const CONFIG_DEFAULT={languages:[],fieldTypes:[]};const internationalizedArray=definePlugin(function(){let config=arguments.length>0&&arguments[0]!==undefined?arguments[0]:CONFIG_DEFAULT;const{apiVersion="2022-11-27",select,languages,fieldTypes}={...CONFIG_DEFAULT,...config};return{name:"sanity-plugin-internationalized-array",// If `languages` is a callback then let's preload it
9489
- studio:Array.isArray(languages)?void 0:{components:{layout:props=>/* @__PURE__ */jsxs(Fragment,{children:[/* @__PURE__ */jsx(Preload,{apiVersion,languages}),props.renderDefault(props)]})}},schema:{types:[...fieldTypes.map(type=>array({type,apiVersion,select,languages})),...fieldTypes.map(type=>object({type}))]}};});export{clear,internationalizedArray};
9491
+ {...type,name:"value",components:{field:InternationalizedField}}],preview:{select:{title:"value",subtitle:"_key"}}});};const CONFIG_DEFAULT={languages:[],defaultLanguages:[],fieldTypes:[]};const internationalizedArray=definePlugin(function(){let config=arguments.length>0&&arguments[0]!==undefined?arguments[0]:CONFIG_DEFAULT;const{apiVersion="2022-11-27",select,languages,fieldTypes,defaultLanguages}={...CONFIG_DEFAULT,...config};return{name:"sanity-plugin-internationalized-array",// If `languages` is a callback then let's preload it
9492
+ studio:Array.isArray(languages)?void 0:{components:{layout:props=>/* @__PURE__ */jsxs(Fragment,{children:[/* @__PURE__ */jsx(Preload,{apiVersion,languages}),props.renderDefault(props)]})}},schema:{types:[...fieldTypes.map(type=>array({type,apiVersion,select,languages,defaultLanguages})),...fieldTypes.map(type=>object({type}))]}};});export{clear,internationalizedArray};
9490
9493
  //# sourceMappingURL=index.esm.js.map