sanity-plugin-internationalized-array 1.4.0 → 1.5.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/lib/index.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- function e(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);n&&(l=l.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,l)}return t}function n(n){for(var l=1;l<arguments.length;l++){var i=null!=arguments[l]?arguments[l]:{};l%2?e(Object(i),!0).forEach((function(e){t(n,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(n,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach((function(e){Object.defineProperty(n,e,Object.getOwnPropertyDescriptor(i,e))}))}return n}function t(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}import{insert as l,setIfMissing as i,set as r,ArrayOfObjectsItem as a,defineField as o,useFormValue as d,unset as u,definePlugin as c}from"sanity";import{jsx as s,jsxs as g,Fragment as p}from"react/jsx-runtime";import h,{useEffect as y,useCallback as f,useMemo as m}from"react";import{Card as v,Stack as b,Text as k,Code as _,useToast as O,Spinner as j,Grid as A,Button as w,Label as P,MenuButton as x,Menu as C,MenuItem as z,Flex as I}from"@sanity/ui";import{AddIcon as S,RemoveIcon as T}from"@sanity/icons";function D(e){return function(e){return e.split(" ").map((e=>e.charAt(0).toUpperCase()+e.slice(1))).join(" ")}(function(e){return e.replace(/-([a-z])/g,(e=>e[1].toUpperCase()))}(e))}function B(e){let n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return n?["internationalizedArray",D(e),"Value"].join(""):["internationalizedArray",D(e)].join("")}const E={languages:[{id:"en",title:"English"},{id:"no",title:"Norsk"}]};function U(){return s(v,{tone:"caution",border:!0,radius:2,padding:3,children:g(b,{space:4,children:[g(k,{children:["An array of language objects must be passed into the ",s("code",{children:"internationalizedArray"})," ","helper function, each with an ",s("code",{children:"id"})," and ",s("code",{children:"title"})," field. Example:"]}),s(v,{padding:2,border:!0,radius:2,children:s(_,{size:1,language:"javascript",children:JSON.stringify(E,null,2)})})]})})}const F=h.createContext({languages:[]}),N=F.Provider;function V(e){const{members:t,value:o,schemaType:d,onChange:u}=e,c="boolean"==typeof d.readOnly&&d.readOnly,{options:v}=d,k=O(),[_,P]=h.useState(Array.isArray(v.languages)?v.languages:null);y((()=>{_||Array.isArray(null==v?void 0:v.languages)||async function(){const e=Array.isArray(v.languages)?v.languages:await v.languages();P(e)}()}),[_,v]);const x=f((e=>{if(!(null==_?void 0:_.length))return;const t={_type:"".concat(d.name,"Value")},r=e?[n(n({},t),{},{_key:e})]:_.filter((e=>!(null==o?void 0:o.length)||!o.find((n=>n._key===e.id)))).map((e=>n(n({},t),{},{_key:e.id}))),a=(null==o?void 0:o.length)?o.map((e=>e)):[],c=r.map((e=>{const n=_.findIndex((n=>e._key===n.id)),t=_.slice(n+1),i=a.findIndex((e=>t.find((n=>n.id===e._key))));return i<0?a.push(e):a.splice(i,0,e),l([e],i<0?"after":"before",[i])}));u([i([]),...c])}),[_,u,d.name,o]),C=f((()=>{if(!(null==o?void 0:o.length)||!(null==_?void 0:_.length))return;const e=o.reduce(((e,n)=>{const t=_.findIndex((e=>e.id===(null==n?void 0:n._key)));return t>-1&&(e[t]=n),e}),[]).filter(Boolean);(null==o?void 0:o.length)!==e.length&&k.push({title:"There was an error reordering languages",status:"warning"}),u(r(e))}),[k,_,u,o]),z=m((()=>!(null==o?void 0:o.length)||!(null==_?void 0:_.length)||(null==o?void 0:o.every((e=>_.find((n=>(null==n?void 0:n.id)===(null==e?void 0:e._key))))))),[o,_]),I=m((()=>_&&_.length>1?_.filter((e=>null==o?void 0:o.find((n=>n._key===e.id)))):[]),[_,o]),T=m((()=>(null==o?void 0:o.length)&&I.length?o.map(((e,n)=>n===I.findIndex((n=>n.id===e._key))?null:e)).filter(Boolean):[]),[o,I]),D=m((()=>!(null==_?void 0:_.length)||(null==_?void 0:_.length)&&_.every((e=>e.id&&e.title))),[_]);return y((()=>{T.length>0&&z&&C()}),[T,z,C]),D?_?s(N,{value:{languages:_},children:g(b,{space:2,children:[(null==t?void 0:t.length)>0?s(p,{children:t.map((n=>"item"===n.kind?s(a,{member:n,renderItem:e.renderItem,renderField:e.renderField,renderInput:e.renderInput,renderPreview:e.renderPreview},n.key):null))}):null,(null==_?void 0:_.length)>0&&I.length<_.length?g(b,{space:2,children:[_.length>1?s(A,{columns:Math.min(_.length,5),gap:2,children:_.map((e=>s(w,{tone:"primary",mode:"ghost",fontSize:1,disabled:c||Boolean(null==o?void 0:o.find((n=>n._key===e.id))),text:e.id.toUpperCase(),icon:S,onClick:()=>x(e.id)},e.id)))}):null,s(w,{tone:"primary",mode:"ghost",disabled:c||o&&(null==o?void 0:o.length)>=(null==_?void 0:_.length),icon:S,text:(null==o?void 0:o.length)?"Add missing ".concat(_.length-o.length==1?"language":"languages"):1===_.length?"Add ".concat(_[0].title," Field"):"Add all languages",onClick:()=>x()})]}):null]})}):s(j,{}):s(U,{})}var J=e=>{const{languages:t,type:l}=e,i="string"==typeof l?l:l.name,r=B(i),a=B(i,!0);return o({name:r,title:"Internationalized array",type:"array",components:{input:V},options:{languages:t},of:[o({name:a,type:a})],validation:e=>e.custom((async(e,t)=>{var l,i,r,a;if(!e)return!0;const o=Array.isArray(null==(i=null==(l=null==t?void 0:t.type)?void 0:l.options)?void 0:i.languages)?null==(r=null==t?void 0:t.type)?void 0:r.options.languages:await(null==(a=null==t?void 0:t.type)?void 0:a.options.languages());if(e&&e.length>o.length)return"Cannot be more than ".concat(1===o.length?"1 item":"".concat(o.length," items"));const d=(null==e?void 0:e.length)?e.filter((e=>!o.find((n=>e._key===n.id)))):[];if(d.length)return{message:"Array item keys must be valid languages registered to the field type",paths:d.map((e=>[{_key:e._key}]))};const u=(null==e?void 0:e.length)?e.filter((e=>Boolean(null==e?void 0:e._key))).reduce(((e,t)=>e[t._key]?n(n({},e),{},{[t._key]:[...e[t._key],t]}):n(n({},e),{},{[t._key]:[t]})),{}):{},c=Object.values(u).filter((e=>(null==e?void 0:e.length)>1)).flat();return!c.length||{message:"There can only be one field per language",paths:c.map((e=>[{_key:e._key}]))}}))})};function L(e){return"reference"===e.schemaType.name&&e.value?e.renderDefault(n(n({},e),{},{title:"",level:0})):e.children}function M(e){if(!(null==e?void 0:e.length))return;const n=e.map((e=>e.level));return n.includes("error")?"critical":n.includes("warning")?"caution":void 0}function q(e){const t=d(e.path.slice(0,-1)),l=n(n({},e.inputProps),{},{members:e.inputProps.members.filter((e=>"field"===e.kind&&"value"===e.name)),value:e.value}),{validation:i,value:a,onChange:o,readOnly:c}=l,{languages:p}=h.useContext(F),y=m((()=>{var e;return null!=(e=null==t?void 0:t.map((e=>e._key)))?e:[]}),[t]),k=!!(null==p?void 0:p.length)&&p.find((e=>e.id===a._key)),_=f((e=>{a&&(null==p?void 0:p.length)&&p.find((n=>n.id===e))&&o([r(e,["_key"])])}),[o,a,p]),O=f((()=>{o(u())}),[o]);return p?s(v,{paddingTop:2,tone:M(i),children:g(b,{space:2,children:[s(v,{tone:"inherit",children:k?s(P,{muted:!0,size:1,children:a._key}):s(x,{button:s(w,{fontSize:1,text:'Change "'.concat(a._key,'"')}),id:"".concat(a._key,"-change-key"),menu:s(C,{children:p.map((e=>s(z,{disabled:y.includes(e.id),fontSize:1,text:e.id.toLocaleUpperCase(),onClick:()=>_(e.id)},e.id)))}),placement:"right",popover:{portal:!0}})}),g(I,{align:"center",gap:2,children:[s(v,{flex:1,tone:"inherit",children:e.inputProps.renderInput(e.inputProps)}),s(v,{tone:"inherit",children:s(w,{mode:"ghost",icon:T,tone:"critical",disabled:c,onClick:O})})]})]})}):s(j,{})}var G=e=>{const{type:t}=e,l=B("string"==typeof t?t:t.name,!0);return o({name:l,title:"Internationalized array ".concat(t),type:"object",components:{item:q},fields:["string"==typeof t?o({name:"value",type:t,components:{field:L}}):n(n({},t),{},{name:"value",components:{field:L}})],preview:{select:{title:"value",subtitle:"_key"}}})};const H={languages:[],fieldTypes:[]},K=c((function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:H;const{languages:t,fieldTypes:l}=n(n({},H),e);return{name:"sanity-plugin-internationalized-array",schema:{types:[...l.map((e=>J({type:e,languages:t}))),...l.map((e=>G({type:e})))]}}}));export{K as internationalizedArray};
1
+ function e(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function n(n){for(var i=1;i<arguments.length;i++){var r=null!=arguments[i]?arguments[i]:{};i%2?e(Object(r),!0).forEach((function(e){t(n,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(n,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(n,e,Object.getOwnPropertyDescriptor(r,e))}))}return n}function t(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}import*as i from"suspend-react";import{suspend as r}from"suspend-react";import{jsx as l,jsxs as a,Fragment as o}from"react/jsx-runtime";import{useClient as d,insert as u,setIfMissing as s,set as c,ArrayOfObjectsItem as p,defineField as g,useFormValue as y,unset as h,definePlugin as f}from"sanity";import m,{memo as v,useCallback as b,useMemo as k,useEffect as _}from"react";import{Card as O,Stack as A,Text as j,Code as P,useToast as w,Grid as C,Button as x,Spinner as z,Label as V,MenuButton as I,Menu as T,MenuItem as D,Flex as S}from"@sanity/ui";import{AddIcon as B,RemoveCircleIcon as E}from"@sanity/icons";const U="sanity-plugin-internationalized-array",F=()=>i.clear(["v0",U]),N=()=>i.peek(["v0",U]);var J=v((function(e){const n=d({apiVersion:e.apiVersion});var t;return Array.isArray(N())||(t=async()=>Array.isArray(e.languages)?e.languages:e.languages(n),i.preload((()=>t()),["v0",U])),null}));function L(e){return function(e){return e.split(" ").map((e=>e.charAt(0).toUpperCase()+e.slice(1))).join(" ")}(function(e){return e.replace(/-([a-z])/g,(e=>e[1].toUpperCase()))}(e))}function M(e){let n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return n?["internationalizedArray",L(e),"Value"].join(""):["internationalizedArray",L(e)].join("")}const q={languages:[{id:"en",title:"English"},{id:"no",title:"Norsk"}]};function G(){return l(O,{tone:"caution",border:!0,radius:2,padding:3,children:a(A,{space:4,children:[a(j,{children:["An array of language objects must be passed into the ",l("code",{children:"internationalizedArray"})," ","helper function, each with an ",l("code",{children:"id"})," and ",l("code",{children:"title"})," field. Example:"]}),l(O,{padding:2,border:!0,radius:2,children:l(P,{size:1,language:"javascript",children:JSON.stringify(q,null,2)})})]})})}const H=m.createContext({languages:[]}),K=H.Provider;function Q(e){const{members:t,value:i,schemaType:g,onChange:y}=e,h="boolean"==typeof g.readOnly&&g.readOnly,{options:f}=g,m=w(),{apiVersion:v}=f,O=d({apiVersion:v}),j=Array.isArray(f.languages)?f.languages:r((async()=>"function"==typeof f.languages?f.languages(O):f.languages),["v0",U]),P=b((e=>{if(!(null==j?void 0:j.length))return;const t={_type:"".concat(g.name,"Value")},r=e?[n(n({},t),{},{_key:e})]:j.filter((e=>!(null==i?void 0:i.length)||!i.find((n=>n._key===e.id)))).map((e=>n(n({},t),{},{_key:e.id}))),l=(null==i?void 0:i.length)?i.map((e=>e)):[],a=r.map((e=>{const n=j.findIndex((n=>e._key===n.id)),t=j.slice(n+1),i=l.findIndex((e=>t.find((n=>n.id===e._key))));return i<0?l.push(e):l.splice(i,0,e),u([e],i<0?"after":"before",[i])}));y([s([]),...a])}),[j,y,g.name,i]),z=b((()=>{if(!(null==i?void 0:i.length)||!(null==j?void 0:j.length))return;const e=i.reduce(((e,n)=>{const t=j.findIndex((e=>e.id===(null==n?void 0:n._key)));return t>-1&&(e[t]=n),e}),[]).filter(Boolean);(null==i?void 0:i.length)!==e.length&&m.push({title:"There was an error reordering languages",status:"warning"}),y(c(e))}),[m,j,y,i]),V=k((()=>!(null==i?void 0:i.length)||!(null==j?void 0:j.length)||(null==i?void 0:i.every((e=>j.find((n=>(null==n?void 0:n.id)===(null==e?void 0:e._key))))))),[i,j]),I=k((()=>j&&j.length>1?j.filter((e=>null==i?void 0:i.find((n=>n._key===e.id)))):[]),[j,i]),T=k((()=>(null==i?void 0:i.length)&&I.length?i.map(((e,n)=>n===I.findIndex((n=>n.id===e._key))?null:e)).filter(Boolean):[]),[i,I]),D=k((()=>!(null==j?void 0:j.length)||(null==j?void 0:j.length)&&j.every((e=>e.id&&e.title))),[j]);return _((()=>{T.length>0&&V&&z()}),[T,V,z]),D?l(K,{value:{languages:j},children:a(A,{space:2,children:[(null==t?void 0:t.length)>0?l(o,{children:t.map((n=>"item"===n.kind?l(p,{member:n,renderItem:e.renderItem,renderField:e.renderField,renderInput:e.renderInput,renderPreview:e.renderPreview},n.key):null))}):null,(null==j?void 0:j.length)>0&&I.length<j.length?a(A,{space:2,children:[j.length>1?l(C,{columns:Math.min(j.length,5),gap:2,children:j.map((e=>l(x,{tone:"primary",mode:"ghost",fontSize:1,disabled:h||Boolean(null==i?void 0:i.find((n=>n._key===e.id))),text:e.id.toUpperCase(),icon:B,onClick:()=>P(e.id)},e.id)))}):null,l(x,{tone:"primary",mode:"ghost",disabled:h||i&&(null==i?void 0:i.length)>=(null==j?void 0:j.length),icon:B,text:(null==i?void 0:i.length)?"Add missing ".concat(j.length-i.length==1?"language":"languages"):1===j.length?"Add ".concat(j[0].title," Field"):"Add all languages",onClick:()=>P()})]}):null]})}):l(G,{})}var R=e=>{const{apiVersion:t,languages:i,type:r}=e,l="string"==typeof r?r:r.name,a=M(l),o=M(l,!0);return g({name:a,title:"Internationalized array",type:"array",components:{input:Q},options:{apiVersion:t,languages:i},of:[g(n(n({},"string"==typeof r?{}:r),{},{name:o,type:o}))],validation:e=>e.custom((async(e,i)=>{var r,l,a;if(!e)return!0;const o=i.getClient({apiVersion:t}),d=Array.isArray(null==(l=null==(r=null==i?void 0:i.type)?void 0:r.options)?void 0:l.languages)?i.type.options.languages:Array.isArray(N())?N():await(null==(a=null==i?void 0:i.type)?void 0:a.options.languages(o));if(e&&e.length>d.length)return"Cannot be more than ".concat(1===d.length?"1 item":"".concat(d.length," items"));const u=(null==e?void 0:e.length)?e.filter((e=>!d.find((n=>e._key===n.id)))):[];if(u.length)return{message:"Array item keys must be valid languages registered to the field type",paths:u.map((e=>[{_key:e._key}]))};const s=(null==e?void 0:e.length)?e.filter((e=>Boolean(null==e?void 0:e._key))).reduce(((e,t)=>e[t._key]?n(n({},e),{},{[t._key]:[...e[t._key],t]}):n(n({},e),{},{[t._key]:[t]})),{}):{},c=Object.values(s).filter((e=>(null==e?void 0:e.length)>1)).flat();return!c.length||{message:"There can only be one field per language",paths:c.map((e=>[{_key:e._key}]))}}))})};function W(e){return"reference"===e.schemaType.name&&e.value?e.renderDefault(n(n({},e),{},{title:"",level:0})):e.children}function X(e){if(!(null==e?void 0:e.length))return;const n=e.map((e=>e.level));return n.includes("error")?"critical":n.includes("warning")?"caution":void 0}function Y(e){const t=y(e.path.slice(0,-1)),i=n(n({},e.inputProps),{},{members:e.inputProps.members.filter((e=>"field"===e.kind&&"value"===e.name)),value:e.value}),{validation:r,value:o,onChange:d,readOnly:u}=i,{languages:s}=m.useContext(H),p=k((()=>{var e;return null!=(e=null==t?void 0:t.map((e=>e._key)))?e:[]}),[t]),g=!!(null==s?void 0:s.length)&&s.find((e=>e.id===o._key)),f=b((e=>{o&&(null==s?void 0:s.length)&&s.find((n=>n.id===e))&&d([c(e,["_key"])])}),[d,o,s]),v=b((()=>{d(h())}),[d]);return s?l(O,{paddingTop:2,tone:X(r),children:a(A,{space:2,children:[l(O,{tone:"inherit",children:g?l(V,{muted:!0,size:1,children:o._key}):l(I,{button:l(x,{fontSize:1,text:'Change "'.concat(o._key,'"')}),id:"".concat(o._key,"-change-key"),menu:l(T,{children:s.map((e=>l(D,{disabled:p.includes(e.id),fontSize:1,text:e.id.toLocaleUpperCase(),onClick:()=>f(e.id)},e.id)))}),placement:"right",popover:{portal:!0}})}),a(S,{align:"center",gap:2,children:[l(O,{flex:1,tone:"inherit",children:e.inputProps.renderInput(e.inputProps)}),l(O,{tone:"inherit",children:l(x,{mode:"bleed",icon:E,tone:"critical",disabled:u,onClick:v})})]})]})}):l(z,{})}var Z=e=>{const{type:t}=e,i=M("string"==typeof t?t:t.name,!0);return g({name:i,title:"Internationalized array ".concat(t),type:"object",components:{item:Y},fields:["string"==typeof t?g({name:"value",type:t,components:{field:W}}):n(n({},t),{},{name:"value",components:{field:W}})],preview:{select:{title:"value",subtitle:"_key"}}})};const $={languages:[],fieldTypes:[]},ee=f((function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:$;const{apiVersion:t="2022-11-27",languages:i,fieldTypes:r}=n(n({},$),e);return{name:"sanity-plugin-internationalized-array",studio:Array.isArray(i)?void 0:{components:{layout:e=>a(o,{children:[l(J,{apiVersion:t,languages:i}),e.renderDefault(e)]})}},schema:{types:[...r.map((e=>R({type:e,apiVersion:t,languages:i}))),...r.map((e=>Z({type:e})))]}}}));export{F as clear,ee as internationalizedArray};
2
2
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/components/createFieldName.ts","../src/components/Feedback.tsx","../src/components/languageContext.tsx","../src/components/InternationalizedArray.tsx","../src/schema/array.ts","../src/components/InternationalizedField.tsx","../src/components/getToneFromValidation.ts","../src/components/InternationalizedInput.tsx","../src/schema/object.ts","../src/plugin.tsx"],"sourcesContent":["export function camelCase(string: string): string {\n return string.replace(/-([a-z])/g, (g) => g[1].toUpperCase())\n}\n\nexport function titleCase(string: string): string {\n return string\n .split(` `)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(` `)\n}\n\nexport function pascalCase(string: string): string {\n return titleCase(camelCase(string))\n}\n\nexport function createFieldName(name: string, addValue = false): string {\n return addValue\n ? [`internationalizedArray`, pascalCase(name), `Value`].join(``)\n : [`internationalizedArray`, pascalCase(name)].join(``)\n}\n","import {Text, Card, Stack, Code} from '@sanity/ui'\nimport React from 'react'\n\nconst schemaExample = {\n languages: [\n {id: 'en', title: 'English'},\n {id: 'no', title: 'Norsk'},\n ],\n}\n\nexport default function Feedback() {\n return (\n <Card tone=\"caution\" border radius={2} padding={3}>\n <Stack space={4}>\n <Text>\n An array of language objects must be passed into the <code>internationalizedArray</code>{' '}\n helper function, each with an <code>id</code> and <code>title</code> field. Example:\n </Text>\n <Card padding={2} border radius={2}>\n <Code size={1} language=\"javascript\">\n {JSON.stringify(schemaExample, null, 2)}\n </Code>\n </Card>\n </Stack>\n </Card>\n )\n}\n","import React from 'react'\n\nimport {Language} from '../types'\n\nexport const LanguageContext = React.createContext<{languages: Language[]}>({\n languages: [],\n})\n\nexport const LanguageProvider = LanguageContext.Provider\n","import React, {useCallback, useEffect, useMemo} from 'react'\nimport {\n insert,\n set,\n setIfMissing,\n ArrayOfObjectsItemMember,\n ArrayOfObjectsItem,\n ArrayOfObjectsInputProps,\n} from 'sanity'\nimport {Button, Grid, Spinner, Stack, useToast} from '@sanity/ui'\nimport {AddIcon} from '@sanity/icons'\n\nimport {Language, Value, ArraySchemaWithLanguageOptions} from '../types'\nimport Feedback from './Feedback'\n// TODO: Move this provider to the root component\nimport {LanguageProvider} from './languageContext'\n\nexport type InternationalizedArrayProps = ArrayOfObjectsInputProps<\n Value,\n ArraySchemaWithLanguageOptions\n>\n\nexport default function InternationalizedArray(props: InternationalizedArrayProps) {\n const {members, value, schemaType, onChange} = props\n const readOnly = typeof schemaType.readOnly === 'boolean' ? schemaType.readOnly : false\n const {options} = schemaType\n const toast = useToast()\n\n const [languages, setLanguages] = React.useState<Language[] | null>(\n Array.isArray(options.languages) ? options.languages : null\n )\n // Resolve async languages\n useEffect(() => {\n async function resolveLanguages() {\n const resolvedLanguages = Array.isArray(options.languages)\n ? options.languages\n : await options.languages()\n setLanguages(resolvedLanguages)\n }\n if (!languages && !Array.isArray(options?.languages)) {\n resolveLanguages()\n }\n }, [languages, options])\n\n const handleAddLanguage = useCallback(\n (languageId?: string) => {\n if (!languages?.length) {\n return\n }\n\n const itemBase = {_type: `${schemaType.name}Value`}\n\n // Create new items\n const newItems = languageId\n ? // Just one for this language\n [{...itemBase, _key: languageId}]\n : // Or one for every missing language\n languages\n .filter((language) =>\n value?.length ? !value.find((v) => v._key === language.id) : true\n )\n .map((language) => ({...itemBase, _key: language.id}))\n\n // Insert new items in the correct order\n const languagesInUse = value?.length ? value.map((v) => v) : []\n\n const insertions = newItems.map((item) => {\n // What's the original index of this language?\n const languageIndex = languages.findIndex((l) => item._key === l.id)\n\n // What languages are there beyond that index?\n const remainingLanguages = languages.slice(languageIndex + 1)\n\n // So what is the index in the current value array of the next language in the language array?\n const nextLanguageIndex = languagesInUse.findIndex((l) =>\n // eslint-disable-next-line max-nested-callbacks\n remainingLanguages.find((r) => r.id === l._key)\n )\n\n // Keep local state up to date incase multiple insertions are being made\n if (nextLanguageIndex < 0) {\n languagesInUse.push(item)\n } else {\n languagesInUse.splice(nextLanguageIndex, 0, item)\n }\n\n return nextLanguageIndex < 0\n ? // No next language (-1), add to end of array\n insert([item], 'after', [nextLanguageIndex])\n : // Next language found, insert before that\n insert([item], 'before', [nextLanguageIndex])\n })\n\n onChange([setIfMissing([]), ...insertions])\n },\n [languages, onChange, schemaType.name, value]\n )\n\n // TODO: This is lazy, reordering and re-setting the whole array – it could be surgical\n const handleRestoreOrder = useCallback(() => {\n if (!value?.length || !languages?.length) {\n return\n }\n\n // Create a new value array in the correct order\n // This would also strip out values that don't have a language as the key\n const updatedValue = value\n .reduce((acc, v) => {\n const newIndex = languages.findIndex((l) => l.id === v?._key)\n\n if (newIndex > -1) {\n acc[newIndex] = v\n }\n\n return acc\n }, [] as Value[])\n .filter(Boolean)\n\n if (value?.length !== updatedValue.length) {\n toast.push({\n title: 'There was an error reordering languages',\n status: 'warning',\n })\n }\n\n onChange(set(updatedValue))\n }, [toast, languages, onChange, value])\n\n const allKeysAreLanguages = useMemo(() => {\n if (!value?.length || !languages?.length) {\n return true\n }\n\n return value?.every((v) => languages.find((l) => l?.id === v?._key))\n }, [value, languages])\n\n // Check languages are in the correct order\n const languagesInUse = useMemo(\n () =>\n languages && languages.length > 1\n ? languages.filter((l) => value?.find((v) => v._key === l.id))\n : [],\n [languages, value]\n )\n\n const languagesOutOfOrder = useMemo(() => {\n if (!value?.length || !languagesInUse.length) {\n return []\n }\n\n return value\n .map((v, vIndex) => (vIndex === languagesInUse.findIndex((l) => l.id === v._key) ? null : v))\n .filter(Boolean)\n }, [value, languagesInUse])\n\n const languagesAreValid = useMemo(\n () =>\n !languages?.length || (languages?.length && languages.every((item) => item.id && item.title)),\n [languages]\n )\n\n useEffect(() => {\n if (languagesOutOfOrder.length > 0 && allKeysAreLanguages) {\n handleRestoreOrder()\n }\n }, [languagesOutOfOrder, allKeysAreLanguages, handleRestoreOrder])\n\n if (!languagesAreValid) {\n return <Feedback />\n }\n\n if (!languages) {\n return <Spinner />\n }\n\n return (\n <LanguageProvider value={{languages}}>\n <Stack space={2}>\n {members?.length > 0 ? (\n <>\n {/* TODO: Resolve type for ArrayOfObjectsItemMember */}\n {/* @ts-ignore */}\n {members.map((member: ArrayOfObjectsItemMember) => {\n if (member.kind === 'item') {\n return (\n <ArrayOfObjectsItem\n key={member.key}\n member={member}\n renderItem={props.renderItem}\n renderField={props.renderField}\n renderInput={props.renderInput}\n renderPreview={props.renderPreview}\n />\n )\n }\n\n return null\n })}\n </>\n ) : null}\n\n {/* This now happens automatically */}\n {/* {languagesOutOfOrder.length > 0 && allKeysAreLanguages ? (\n <Button\n tone=\"caution\"\n icon={RestoreIcon}\n onClick={() => handleRestoreOrder()}\n text=\"Restore order of languages\"\n />\n ) : null} */}\n\n {/* Show buttons if languages are configured */}\n {/* Hide them if all languages have values */}\n {languages?.length > 0 && languagesInUse.length < languages.length ? (\n <Stack space={2}>\n {/* Hide language-specific buttons if there's only one */}\n {/* No more than 5 columns */}\n {languages.length > 1 ? (\n <Grid columns={Math.min(languages.length, 5)} gap={2}>\n {languages.map((language) => (\n <Button\n key={language.id}\n tone=\"primary\"\n mode=\"ghost\"\n fontSize={1}\n disabled={readOnly || Boolean(value?.find((item) => item._key === language.id))}\n text={language.id.toUpperCase()}\n icon={AddIcon}\n onClick={() => handleAddLanguage(language.id)}\n />\n ))}\n </Grid>\n ) : null}\n <Button\n tone=\"primary\"\n mode=\"ghost\"\n disabled={readOnly || (value && value?.length >= languages?.length)}\n icon={AddIcon}\n text={\n // eslint-disable-next-line no-nested-ternary\n value?.length\n ? `Add missing ${\n languages.length - value.length === 1 ? `language` : `languages`\n }`\n : languages.length === 1\n ? `Add ${languages[0].title} Field`\n : `Add all languages`\n }\n onClick={() => handleAddLanguage()}\n />\n </Stack>\n ) : null}\n </Stack>\n </LanguageProvider>\n )\n}\n","import {defineField, FieldDefinition, Rule} from 'sanity'\n\nimport {createFieldName} from '../components/createFieldName'\nimport InternationalizedArray from '../components/InternationalizedArray'\nimport {Language, Value} from '../types'\n\ntype ArrayFactoryConfig = {\n languages: Language[] | (() => Promise<Language[]>)\n type: string | FieldDefinition\n}\n\nexport default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {\n const {languages, type} = config\n const typeName = typeof type === `string` ? type : type.name\n const arrayName = createFieldName(typeName)\n const objectName = createFieldName(typeName, true)\n\n return defineField({\n name: arrayName,\n title: 'Internationalized array',\n type: 'array',\n // TODO: Resolve this typing issue with the outer component\n // @ts-ignore\n components: {\n input: InternationalizedArray,\n },\n options: {languages},\n // TODO: Resolve this typing issue with the inner object\n // @ts-ignore\n of: [\n defineField({\n name: objectName,\n type: objectName,\n }),\n ],\n validation: (rule: Rule) =>\n rule.custom<Value[]>(async (value, context) => {\n if (!value) {\n return true\n }\n\n const contextLanguages: Language[] = Array.isArray(context?.type?.options?.languages)\n ? context?.type?.options.languages\n : await context?.type?.options.languages()\n\n if (value && value.length > contextLanguages.length) {\n return `Cannot be more than ${\n contextLanguages.length === 1 ? `1 item` : `${contextLanguages.length} items`\n }`\n }\n\n const nonLanguageKeys = value?.length\n ? value.filter((item) => !contextLanguages.find((language) => item._key === language.id))\n : []\n if (nonLanguageKeys.length) {\n return {\n message: `Array item keys must be valid languages registered to the field type`,\n paths: nonLanguageKeys.map((item) => [{_key: item._key}]),\n }\n }\n\n // Ensure there's no duplicate `language` fields\n type KeyedValues = {\n [key: string]: Value[]\n }\n\n const valuesByLanguage = value?.length\n ? value\n .filter((item) => Boolean(item?._key))\n .reduce((acc, cur) => {\n if (acc[cur._key]) {\n return {...acc, [cur._key]: [...acc[cur._key], cur]}\n }\n return {\n ...acc,\n [cur._key]: [cur],\n }\n }, {} as KeyedValues)\n : {}\n const duplicateValues = Object.values(valuesByLanguage)\n .filter((item) => item?.length > 1)\n .flat()\n if (duplicateValues.length) {\n return {\n message: 'There can only be one field per language',\n paths: duplicateValues.map((item) => [{_key: item._key}]),\n }\n }\n\n return true\n }),\n })\n}\n","import {FieldProps} from 'sanity'\n\nexport default function InternationalizedField(props: FieldProps) {\n // Show reference field selector if there's a value\n if (props.schemaType.name === 'reference' && props.value) {\n return props.renderDefault({\n ...props,\n title: '',\n level: 0,\n })\n }\n\n return props.children\n}\n","import {CardTone} from '@sanity/ui'\nimport {FormNodeValidation} from 'sanity'\n\nexport function getToneFromValidation(validations: FormNodeValidation[]): CardTone | undefined {\n if (!validations?.length) {\n return undefined\n }\n\n const validationLevels = validations.map((v) => v.level)\n\n if (validationLevels.includes('error')) {\n return `critical`\n } else if (validationLevels.includes('warning')) {\n return `caution`\n }\n\n return undefined\n}\n","import {ObjectItemProps, useFormValue} from 'sanity'\nimport React, {useCallback, useMemo} from 'react'\nimport {unset, set} from 'sanity'\nimport {Button, Flex, Label, MenuButton, Menu, MenuItem, Card, Spinner, Stack} from '@sanity/ui'\nimport {RemoveIcon} from '@sanity/icons'\n\nimport {getToneFromValidation} from './getToneFromValidation'\nimport {LanguageContext} from './languageContext'\n\ntype InternationalizedValue = {\n _type: string\n _key: string\n value: string\n}\n\nexport default function InternationalizedInput(props: ObjectItemProps<InternationalizedValue>) {\n const parentValue = useFormValue(props.path.slice(0, -1)) as InternationalizedValue[]\n\n const inlineProps = {\n ...props.inputProps,\n // This is the magic that makes inline editing work?\n members: props.inputProps.members.filter((m) => m.kind === 'field' && m.name === 'value'),\n // This just overrides the type\n // TODO: Remove this as it shouldn't be necessary?\n value: props.value as InternationalizedValue,\n }\n\n const {validation, value, onChange, readOnly} = inlineProps\n\n // The parent array contains the languages from the plugin config\n const {languages} = React.useContext(LanguageContext)\n\n const languageKeysInUse = useMemo(() => parentValue?.map((v) => v._key) ?? [], [parentValue])\n const keyIsValid = languages?.length ? languages.find((l) => l.id === value._key) : false\n\n // Changes the key of this item, ideally to a valid language\n const handleKeyChange = useCallback(\n (languageId: string) => {\n if (!value || !languages?.length || !languages.find((l) => l.id === languageId)) {\n return\n }\n\n onChange([set(languageId, ['_key'])])\n },\n [onChange, value, languages]\n )\n\n // Removes this item from the array\n const handleUnset = useCallback(() => {\n onChange(unset())\n }, [onChange])\n\n if (!languages) {\n return <Spinner />\n }\n\n return (\n <Card paddingTop={2} tone={getToneFromValidation(validation)}>\n <Stack space={2}>\n <Card tone=\"inherit\">\n {keyIsValid ? (\n <Label muted size={1}>\n {value._key}\n </Label>\n ) : (\n <MenuButton\n button={<Button fontSize={1} text={`Change \"${value._key}\"`} />}\n id={`${value._key}-change-key`}\n menu={\n <Menu>\n {languages.map((language) => (\n <MenuItem\n disabled={languageKeysInUse.includes(language.id)}\n fontSize={1}\n key={language.id}\n text={language.id.toLocaleUpperCase()}\n onClick={() => handleKeyChange(language.id)}\n />\n ))}\n </Menu>\n }\n placement=\"right\"\n popover={{portal: true}}\n />\n )}\n </Card>\n <Flex align=\"center\" gap={2}>\n <Card flex={1} tone=\"inherit\">\n {props.inputProps.renderInput(props.inputProps)}\n </Card>\n\n <Card tone=\"inherit\">\n <Button\n mode=\"ghost\"\n icon={RemoveIcon}\n tone=\"critical\"\n disabled={readOnly}\n onClick={handleUnset}\n />\n </Card>\n </Flex>\n </Stack>\n </Card>\n )\n}\n","import {defineField, FieldDefinition} from 'sanity'\n\nimport {createFieldName} from '../components/createFieldName'\nimport InternationalizedField from '../components/InternationalizedField'\nimport InternationalizedInput from '../components/InternationalizedInput'\n\ntype ObjectFactoryConfig = {\n type: string | FieldDefinition\n}\n\nexport default (config: ObjectFactoryConfig): FieldDefinition<'object'> => {\n const {type} = config\n const typeName = typeof type === `string` ? type : type.name\n const objectName = createFieldName(typeName, true)\n\n return defineField({\n name: objectName,\n title: `Internationalized array ${type}`,\n type: 'object',\n // TODO: Resolve this typing issue with the return type\n // @ts-ignore\n components: {\n // TODO: Resolve this typing issue with the outer component\n // @ts-ignore\n item: InternationalizedInput,\n },\n // TODO: Address this typing issue with the inner object\n // @ts-ignore\n fields: [\n typeof type === `string`\n ? // Define a simple field if all we have is the name as a string\n defineField({\n name: 'value',\n type,\n // TODO: Address this typing issue with components on a dynamic `type`\n // @ts-ignore\n components: {\n // TODO: Address this typing issue with the inner object\n // @ts-ignore\n field: InternationalizedField,\n },\n })\n : // Pass in the configured options, but overwrite the name\n {\n ...type,\n name: 'value',\n components: {\n field: InternationalizedField,\n },\n },\n ],\n preview: {\n select: {\n title: 'value',\n subtitle: '_key',\n },\n },\n })\n}\n","import {definePlugin} from 'sanity'\nimport {PluginConfig} from './types'\nimport array from './schema/array'\nimport object from './schema/object'\n\nconst CONFIG_DEFAULT = {\n languages: [],\n fieldTypes: [],\n}\n\nexport const internationalizedArray = definePlugin<PluginConfig>((config = CONFIG_DEFAULT) => {\n const {languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}\n\n return {\n name: 'sanity-plugin-internationalized-array',\n schema: {\n types: [\n ...fieldTypes.map((type) => array({type, languages})),\n ...fieldTypes.map((type) => object({type})),\n ],\n },\n }\n})\n"],"names":["pascalCase","string","split","map","word","charAt","toUpperCase","slice","join","titleCase","replace","g","camelCase","createFieldName","name","addValue","schemaExample","languages","id","title","Feedback","jsx","Card","tone","border","radius","padding","children","jsxs","Stack","space","Text","Code","size","language","JSON","stringify","LanguageContext","React","createContext","LanguageProvider","Provider","InternationalizedArray","props","members","value","schemaType","onChange","readOnly","options","toast","useToast","setLanguages","useState","Array","isArray","useEffect","async","resolvedLanguages","resolveLanguages","handleAddLanguage","useCallback","languageId","length","itemBase","_type","concat","newItems","_key","filter","find","v","languagesInUse","insertions","item","languageIndex","findIndex","l","remainingLanguages","nextLanguageIndex","r","push","splice","insert","setIfMissing","handleRestoreOrder","updatedValue","reduce","acc","newIndex","Boolean","status","set","allKeysAreLanguages","useMemo","every","languagesOutOfOrder","vIndex","languagesAreValid","Fragment","member","kind","ArrayOfObjectsItem","renderItem","renderField","renderInput","renderPreview","key","Grid","columns","Math","min","gap","Button","mode","fontSize","disabled","text","icon","AddIcon","onClick","Spinner","array","config","type","typeName","arrayName","objectName","defineField","components","input","of","validation","rule","custom","context","_a","_b","_c","_d","contextLanguages","nonLanguageKeys","message","paths","valuesByLanguage","cur","_objectSpread","duplicateValues","Object","values","flat","InternationalizedField","renderDefault","level","getToneFromValidation","validations","validationLevels","includes","InternationalizedInput","parentValue","useFormValue","path","inlineProps","inputProps","m","useContext","languageKeysInUse","keyIsValid","handleKeyChange","handleUnset","unset","paddingTop","Label","muted","MenuButton","button","menu","Menu","MenuItem","toLocaleUpperCase","placement","popover","portal","Flex","align","flex","RemoveIcon","object","fields","field","preview","select","subtitle","CONFIG_DEFAULT","fieldTypes","internationalizedArray","definePlugin","schema","types"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/cache.ts","../src/components/Preload.tsx","../src/components/createFieldName.ts","../src/components/Feedback.tsx","../src/components/languageContext.tsx","../src/components/InternationalizedArray.tsx","../src/schema/array.ts","../src/components/InternationalizedField.tsx","../src/components/getToneFromValidation.ts","../src/components/InternationalizedInput.tsx","../src/schema/object.ts","../src/plugin.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\n\nimport * as suspend from 'suspend-react'\nimport type {Language} from './types'\n\nexport const namespace = 'sanity-plugin-internationalized-array'\n\nexport const version = 'v0'\n\n// https://github.com/pmndrs/suspend-react#preloading\nexport const preload = (fn: () => Promise<Language[]>) =>\n suspend.preload(() => fn(), [version, namespace])\n\n// https://github.com/pmndrs/suspend-react#cache-busting\nexport const clear = () => suspend.clear([version, namespace])\n\n// https://github.com/pmndrs/suspend-react#peeking-into-entries-outside-of-suspense\nexport const peek = () => suspend.peek([version, namespace]) as Language[] | undefined\n","import {peek, preload} from '../cache'\nimport {memo} from 'react'\nimport type {PluginConfig} from '../types'\nimport {useClient} from 'sanity'\n\nexport default memo(function Preload(\n props: Required<Pick<PluginConfig, 'apiVersion' | 'languages'>>\n) {\n const client = useClient({apiVersion: props.apiVersion})\n if (!Array.isArray(peek())) {\n // eslint-disable-next-line require-await\n preload(async () =>\n Array.isArray(props.languages) ? props.languages : props.languages(client)\n )\n }\n\n return null\n})\n","export function camelCase(string: string): string {\n return string.replace(/-([a-z])/g, (g) => g[1].toUpperCase())\n}\n\nexport function titleCase(string: string): string {\n return string\n .split(` `)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(` `)\n}\n\nexport function pascalCase(string: string): string {\n return titleCase(camelCase(string))\n}\n\nexport function createFieldName(name: string, addValue = false): string {\n return addValue\n ? [`internationalizedArray`, pascalCase(name), `Value`].join(``)\n : [`internationalizedArray`, pascalCase(name)].join(``)\n}\n","import {Text, Card, Stack, Code} from '@sanity/ui'\nimport React from 'react'\n\nconst schemaExample = {\n languages: [\n {id: 'en', title: 'English'},\n {id: 'no', title: 'Norsk'},\n ],\n}\n\nexport default function Feedback() {\n return (\n <Card tone=\"caution\" border radius={2} padding={3}>\n <Stack space={4}>\n <Text>\n An array of language objects must be passed into the <code>internationalizedArray</code>{' '}\n helper function, each with an <code>id</code> and <code>title</code> field. Example:\n </Text>\n <Card padding={2} border radius={2}>\n <Code size={1} language=\"javascript\">\n {JSON.stringify(schemaExample, null, 2)}\n </Code>\n </Card>\n </Stack>\n </Card>\n )\n}\n","import React from 'react'\n\nimport {Language} from '../types'\n\nexport const LanguageContext = React.createContext<{languages: Language[]}>({\n languages: [],\n})\n\nexport const LanguageProvider = LanguageContext.Provider\n","import React, {Fragment, useCallback, useEffect, useMemo} from 'react'\nimport {\n insert,\n set,\n setIfMissing,\n ArrayOfObjectsItemMember,\n ArrayOfObjectsItem,\n ArrayOfObjectsInputProps,\n useClient,\n} from 'sanity'\nimport {Button, Grid, Stack, useToast} from '@sanity/ui'\nimport {AddIcon} from '@sanity/icons'\nimport {suspend} from 'suspend-react'\n\nimport type {Value, ArraySchemaWithLanguageOptions} from '../types'\nimport Feedback from './Feedback'\n// TODO: Move this provider to the root component\nimport {LanguageProvider} from './languageContext'\nimport {namespace, version} from '../cache'\n\nexport type InternationalizedArrayProps = ArrayOfObjectsInputProps<\n Value,\n ArraySchemaWithLanguageOptions\n>\n\nexport default function InternationalizedArray(props: InternationalizedArrayProps) {\n const {members, value, schemaType, onChange} = props\n const readOnly = typeof schemaType.readOnly === 'boolean' ? schemaType.readOnly : false\n const {options} = schemaType\n const toast = useToast()\n\n const {apiVersion} = options\n const client = useClient({apiVersion})\n const languages = Array.isArray(options.languages)\n ? options.languages\n : // eslint-disable-next-line require-await\n suspend(async () => {\n if (typeof options.languages === 'function') {\n return options.languages(client)\n }\n return options.languages\n }, [version, namespace])\n\n const handleAddLanguage = useCallback(\n (languageId?: string) => {\n if (!languages?.length) {\n return\n }\n\n const itemBase = {_type: `${schemaType.name}Value`}\n\n // Create new items\n const newItems = languageId\n ? // Just one for this language\n [{...itemBase, _key: languageId}]\n : // Or one for every missing language\n languages\n .filter((language) =>\n value?.length ? !value.find((v) => v._key === language.id) : true\n )\n .map((language) => ({...itemBase, _key: language.id}))\n\n // Insert new items in the correct order\n const languagesInUse = value?.length ? value.map((v) => v) : []\n\n const insertions = newItems.map((item) => {\n // What's the original index of this language?\n const languageIndex = languages.findIndex((l) => item._key === l.id)\n\n // What languages are there beyond that index?\n const remainingLanguages = languages.slice(languageIndex + 1)\n\n // So what is the index in the current value array of the next language in the language array?\n const nextLanguageIndex = languagesInUse.findIndex((l) =>\n // eslint-disable-next-line max-nested-callbacks\n remainingLanguages.find((r) => r.id === l._key)\n )\n\n // Keep local state up to date incase multiple insertions are being made\n if (nextLanguageIndex < 0) {\n languagesInUse.push(item)\n } else {\n languagesInUse.splice(nextLanguageIndex, 0, item)\n }\n\n return nextLanguageIndex < 0\n ? // No next language (-1), add to end of array\n insert([item], 'after', [nextLanguageIndex])\n : // Next language found, insert before that\n insert([item], 'before', [nextLanguageIndex])\n })\n\n onChange([setIfMissing([]), ...insertions])\n },\n [languages, onChange, schemaType.name, value]\n )\n\n // TODO: This is lazy, reordering and re-setting the whole array – it could be surgical\n const handleRestoreOrder = useCallback(() => {\n if (!value?.length || !languages?.length) {\n return\n }\n\n // Create a new value array in the correct order\n // This would also strip out values that don't have a language as the key\n const updatedValue = value\n .reduce((acc, v) => {\n const newIndex = languages.findIndex((l) => l.id === v?._key)\n\n if (newIndex > -1) {\n acc[newIndex] = v\n }\n\n return acc\n }, [] as Value[])\n .filter(Boolean)\n\n if (value?.length !== updatedValue.length) {\n toast.push({\n title: 'There was an error reordering languages',\n status: 'warning',\n })\n }\n\n onChange(set(updatedValue))\n }, [toast, languages, onChange, value])\n\n const allKeysAreLanguages = useMemo(() => {\n if (!value?.length || !languages?.length) {\n return true\n }\n\n return value?.every((v) => languages.find((l) => l?.id === v?._key))\n }, [value, languages])\n\n // Check languages are in the correct order\n const languagesInUse = useMemo(\n () =>\n languages && languages.length > 1\n ? languages.filter((l) => value?.find((v) => v._key === l.id))\n : [],\n [languages, value]\n )\n\n const languagesOutOfOrder = useMemo(() => {\n if (!value?.length || !languagesInUse.length) {\n return []\n }\n\n return value\n .map((v, vIndex) => (vIndex === languagesInUse.findIndex((l) => l.id === v._key) ? null : v))\n .filter(Boolean)\n }, [value, languagesInUse])\n\n const languagesAreValid = useMemo(\n () =>\n !languages?.length || (languages?.length && languages.every((item) => item.id && item.title)),\n [languages]\n )\n\n useEffect(() => {\n if (languagesOutOfOrder.length > 0 && allKeysAreLanguages) {\n handleRestoreOrder()\n }\n }, [languagesOutOfOrder, allKeysAreLanguages, handleRestoreOrder])\n\n if (!languagesAreValid) {\n return <Feedback />\n }\n\n return (\n <LanguageProvider value={{languages}}>\n <Stack space={2}>\n {members?.length > 0 ? (\n <>\n {/* TODO: Resolve type for ArrayOfObjectsItemMember */}\n {/* @ts-ignore */}\n {members.map((member: ArrayOfObjectsItemMember) => {\n if (member.kind === 'item') {\n return (\n <ArrayOfObjectsItem\n key={member.key}\n member={member}\n renderItem={props.renderItem}\n renderField={props.renderField}\n renderInput={props.renderInput}\n renderPreview={props.renderPreview}\n />\n )\n }\n\n return null\n })}\n </>\n ) : null}\n\n {/* This now happens automatically */}\n {/* {languagesOutOfOrder.length > 0 && allKeysAreLanguages ? (\n <Button\n tone=\"caution\"\n icon={RestoreIcon}\n onClick={() => handleRestoreOrder()}\n text=\"Restore order of languages\"\n />\n ) : null} */}\n\n {/* Show buttons if languages are configured */}\n {/* Hide them if all languages have values */}\n {languages?.length > 0 && languagesInUse.length < languages.length ? (\n <Stack space={2}>\n {/* Hide language-specific buttons if there's only one */}\n {/* No more than 5 columns */}\n {languages.length > 1 ? (\n <Grid columns={Math.min(languages.length, 5)} gap={2}>\n {languages.map((language) => (\n <Button\n key={language.id}\n tone=\"primary\"\n mode=\"ghost\"\n fontSize={1}\n disabled={readOnly || Boolean(value?.find((item) => item._key === language.id))}\n text={language.id.toUpperCase()}\n icon={AddIcon}\n onClick={() => handleAddLanguage(language.id)}\n />\n ))}\n </Grid>\n ) : null}\n <Button\n tone=\"primary\"\n mode=\"ghost\"\n disabled={readOnly || (value && value?.length >= languages?.length)}\n icon={AddIcon}\n text={\n // eslint-disable-next-line no-nested-ternary\n value?.length\n ? `Add missing ${\n languages.length - value.length === 1 ? `language` : `languages`\n }`\n : languages.length === 1\n ? `Add ${languages[0].title} Field`\n : `Add all languages`\n }\n onClick={() => handleAddLanguage()}\n />\n </Stack>\n ) : null}\n </Stack>\n </LanguageProvider>\n )\n}\n","/* eslint-disable no-nested-ternary */\nimport {defineField, type FieldDefinition, type Rule, type SanityClient} from 'sanity'\nimport {peek} from '../cache'\n\nimport {createFieldName} from '../components/createFieldName'\nimport InternationalizedArray from '../components/InternationalizedArray'\nimport {Language, Value} from '../types'\n\ntype ArrayFactoryConfig = {\n apiVersion: string\n languages: Language[] | ((client: SanityClient) => Promise<Language[]>)\n type: string | FieldDefinition\n}\n\nexport default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {\n const {apiVersion, languages, type} = config\n const typeName = typeof type === `string` ? type : type.name\n const arrayName = createFieldName(typeName)\n const objectName = createFieldName(typeName, true)\n\n return defineField({\n name: arrayName,\n title: 'Internationalized array',\n type: 'array',\n // TODO: Resolve this typing issue with the outer component\n // @ts-ignore\n components: {\n input: InternationalizedArray,\n },\n options: {apiVersion, languages},\n // TODO: Resolve this typing issue with the inner object\n // @ts-ignore\n of: [\n defineField({\n ...(typeof type === 'string' ? {} : type),\n name: objectName,\n type: objectName,\n }),\n ],\n validation: (rule: Rule) =>\n rule.custom<Value[]>(async (value, context) => {\n if (!value) {\n return true\n }\n\n const client = context.getClient({apiVersion})\n const contextLanguages: Language[] = Array.isArray(context?.type?.options?.languages)\n ? context!.type!.options.languages\n : Array.isArray(peek())\n ? peek()\n : await context?.type?.options.languages(client)\n\n if (value && value.length > contextLanguages.length) {\n return `Cannot be more than ${\n contextLanguages.length === 1 ? `1 item` : `${contextLanguages.length} items`\n }`\n }\n\n const nonLanguageKeys = value?.length\n ? value.filter((item) => !contextLanguages.find((language) => item._key === language.id))\n : []\n if (nonLanguageKeys.length) {\n return {\n message: `Array item keys must be valid languages registered to the field type`,\n paths: nonLanguageKeys.map((item) => [{_key: item._key}]),\n }\n }\n\n // Ensure there's no duplicate `language` fields\n type KeyedValues = {\n [key: string]: Value[]\n }\n\n const valuesByLanguage = value?.length\n ? value\n .filter((item) => Boolean(item?._key))\n .reduce((acc, cur) => {\n if (acc[cur._key]) {\n return {...acc, [cur._key]: [...acc[cur._key], cur]}\n }\n return {\n ...acc,\n [cur._key]: [cur],\n }\n }, {} as KeyedValues)\n : {}\n const duplicateValues = Object.values(valuesByLanguage)\n .filter((item) => item?.length > 1)\n .flat()\n if (duplicateValues.length) {\n return {\n message: 'There can only be one field per language',\n paths: duplicateValues.map((item) => [{_key: item._key}]),\n }\n }\n\n return true\n }),\n })\n}\n","import {FieldProps} from 'sanity'\n\nexport default function InternationalizedField(props: FieldProps) {\n // Show reference field selector if there's a value\n if (props.schemaType.name === 'reference' && props.value) {\n return props.renderDefault({\n ...props,\n title: '',\n level: 0,\n })\n }\n\n return props.children\n}\n","import {CardTone} from '@sanity/ui'\nimport {FormNodeValidation} from 'sanity'\n\nexport function getToneFromValidation(validations: FormNodeValidation[]): CardTone | undefined {\n if (!validations?.length) {\n return undefined\n }\n\n const validationLevels = validations.map((v) => v.level)\n\n if (validationLevels.includes('error')) {\n return `critical`\n } else if (validationLevels.includes('warning')) {\n return `caution`\n }\n\n return undefined\n}\n","import {ObjectItemProps, useFormValue} from 'sanity'\nimport React, {useCallback, useMemo} from 'react'\nimport {unset, set} from 'sanity'\nimport {Button, Flex, Label, MenuButton, Menu, MenuItem, Card, Spinner, Stack} from '@sanity/ui'\nimport {RemoveCircleIcon} from '@sanity/icons'\n\nimport {getToneFromValidation} from './getToneFromValidation'\nimport {LanguageContext} from './languageContext'\n\ntype InternationalizedValue = {\n _type: string\n _key: string\n value: string\n}\n\nexport default function InternationalizedInput(props: ObjectItemProps<InternationalizedValue>) {\n const parentValue = useFormValue(props.path.slice(0, -1)) as InternationalizedValue[]\n\n const inlineProps = {\n ...props.inputProps,\n // This is the magic that makes inline editing work?\n members: props.inputProps.members.filter((m) => m.kind === 'field' && m.name === 'value'),\n // This just overrides the type\n // TODO: Remove this as it shouldn't be necessary?\n value: props.value as InternationalizedValue,\n }\n\n const {validation, value, onChange, readOnly} = inlineProps\n\n // The parent array contains the languages from the plugin config\n const {languages} = React.useContext(LanguageContext)\n\n const languageKeysInUse = useMemo(() => parentValue?.map((v) => v._key) ?? [], [parentValue])\n const keyIsValid = languages?.length ? languages.find((l) => l.id === value._key) : false\n\n // Changes the key of this item, ideally to a valid language\n const handleKeyChange = useCallback(\n (languageId: string) => {\n if (!value || !languages?.length || !languages.find((l) => l.id === languageId)) {\n return\n }\n\n onChange([set(languageId, ['_key'])])\n },\n [onChange, value, languages]\n )\n\n // Removes this item from the array\n const handleUnset = useCallback(() => {\n onChange(unset())\n }, [onChange])\n\n if (!languages) {\n return <Spinner />\n }\n\n return (\n <Card paddingTop={2} tone={getToneFromValidation(validation)}>\n <Stack space={2}>\n <Card tone=\"inherit\">\n {keyIsValid ? (\n <Label muted size={1}>\n {value._key}\n </Label>\n ) : (\n <MenuButton\n button={<Button fontSize={1} text={`Change \"${value._key}\"`} />}\n id={`${value._key}-change-key`}\n menu={\n <Menu>\n {languages.map((language) => (\n <MenuItem\n disabled={languageKeysInUse.includes(language.id)}\n fontSize={1}\n key={language.id}\n text={language.id.toLocaleUpperCase()}\n onClick={() => handleKeyChange(language.id)}\n />\n ))}\n </Menu>\n }\n placement=\"right\"\n popover={{portal: true}}\n />\n )}\n </Card>\n <Flex align=\"center\" gap={2}>\n <Card flex={1} tone=\"inherit\">\n {props.inputProps.renderInput(props.inputProps)}\n </Card>\n\n <Card tone=\"inherit\">\n <Button\n mode=\"bleed\"\n icon={RemoveCircleIcon}\n tone=\"critical\"\n disabled={readOnly}\n onClick={handleUnset}\n />\n </Card>\n </Flex>\n </Stack>\n </Card>\n )\n}\n","import {defineField, FieldDefinition} from 'sanity'\n\nimport {createFieldName} from '../components/createFieldName'\nimport InternationalizedField from '../components/InternationalizedField'\nimport InternationalizedInput from '../components/InternationalizedInput'\n\ntype ObjectFactoryConfig = {\n type: string | FieldDefinition\n}\n\nexport default (config: ObjectFactoryConfig): FieldDefinition<'object'> => {\n const {type} = config\n const typeName = typeof type === `string` ? type : type.name\n const objectName = createFieldName(typeName, true)\n\n return defineField({\n name: objectName,\n title: `Internationalized array ${type}`,\n type: 'object',\n // TODO: Resolve this typing issue with the return type\n // @ts-ignore\n components: {\n // TODO: Resolve this typing issue with the outer component\n // @ts-ignore\n item: InternationalizedInput,\n },\n // TODO: Address this typing issue with the inner object\n // @ts-ignore\n fields: [\n typeof type === `string`\n ? // Define a simple field if all we have is the name as a string\n defineField({\n name: 'value',\n type,\n // TODO: Address this typing issue with components on a dynamic `type`\n // @ts-ignore\n components: {\n // TODO: Address this typing issue with the inner object\n // @ts-ignore\n field: InternationalizedField,\n },\n })\n : // Pass in the configured options, but overwrite the name\n {\n ...type,\n name: 'value',\n components: {\n field: InternationalizedField,\n },\n },\n ],\n preview: {\n select: {\n title: 'value',\n subtitle: '_key',\n },\n },\n })\n}\n","import React from 'react'\nimport {definePlugin} from 'sanity'\nimport Preload from './components/Preload'\nimport array from './schema/array'\nimport object from './schema/object'\nimport {PluginConfig} from './types'\n\nconst CONFIG_DEFAULT: PluginConfig = {\n languages: [],\n fieldTypes: [],\n}\n\nexport const internationalizedArray = definePlugin<PluginConfig>((config = CONFIG_DEFAULT) => {\n const {apiVersion = '2022-11-27', languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}\n\n return {\n name: 'sanity-plugin-internationalized-array',\n // If `languages` is a callback then let's preload it\n studio: Array.isArray(languages)\n ? undefined\n : {\n components: {\n layout: (props) => (\n <>\n <Preload apiVersion={apiVersion} languages={languages} />\n {props.renderDefault(props)}\n </>\n ),\n },\n },\n schema: {\n types: [\n ...fieldTypes.map((type) => array({type, apiVersion, languages})),\n ...fieldTypes.map((type) => object({type})),\n ],\n },\n }\n})\n"],"names":["namespace","clear","suspend","peek","Preload","memo","props","client","useClient","apiVersion","fn","Array","isArray","async","languages","preload","pascalCase","string","split","map","word","charAt","toUpperCase","slice","join","titleCase","replace","g","camelCase","createFieldName","name","addValue","schemaExample","id","title","Feedback","jsx","Card","tone","border","radius","padding","children","jsxs","Stack","space","Text","Code","size","language","JSON","stringify","LanguageContext","React","createContext","LanguageProvider","Provider","InternationalizedArray","members","value","schemaType","onChange","readOnly","options","toast","useToast","handleAddLanguage","useCallback","languageId","length","itemBase","_type","concat","newItems","_key","filter","find","v","languagesInUse","insertions","item","languageIndex","findIndex","l","remainingLanguages","nextLanguageIndex","r","push","splice","insert","setIfMissing","handleRestoreOrder","updatedValue","reduce","acc","newIndex","Boolean","status","set","allKeysAreLanguages","useMemo","every","languagesOutOfOrder","vIndex","languagesAreValid","useEffect","Fragment","member","kind","ArrayOfObjectsItem","renderItem","renderField","renderInput","renderPreview","key","Grid","columns","Math","min","gap","Button","mode","fontSize","disabled","text","icon","AddIcon","onClick","array","config","type","typeName","arrayName","objectName","defineField","components","input","of","_objectSpread","validation","rule","custom","context","_a","_b","_c","getClient","contextLanguages","nonLanguageKeys","message","paths","valuesByLanguage","cur","duplicateValues","Object","values","flat","InternationalizedField","renderDefault","level","getToneFromValidation","validations","validationLevels","includes","InternationalizedInput","parentValue","useFormValue","path","inlineProps","inputProps","m","useContext","languageKeysInUse","keyIsValid","handleKeyChange","handleUnset","unset","paddingTop","Label","muted","MenuButton","button","menu","Menu","MenuItem","toLocaleUpperCase","placement","popover","portal","Flex","align","flex","RemoveCircleIcon","Spinner","object","fields","field","preview","select","subtitle","CONFIG_DEFAULT","fieldTypes","internationalizedArray","definePlugin","studio","layout","schema","types"],"mappings":"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"}
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";function e(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);n&&(l=l.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,l)}return t}function n(n){for(var l=1;l<arguments.length;l++){var i=null!=arguments[l]?arguments[l]:{};l%2?e(Object(i),!0).forEach((function(e){t(n,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(n,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach((function(e){Object.defineProperty(n,e,Object.getOwnPropertyDescriptor(i,e))}))}return n}function t(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}Object.defineProperty(exports,"__esModule",{value:!0});var l=require("sanity"),i=require("react/jsx-runtime"),r=require("react"),a=require("@sanity/ui"),o=require("@sanity/icons");function s(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var d=s(r);function u(e){return function(e){return e.split(" ").map((e=>e.charAt(0).toUpperCase()+e.slice(1))).join(" ")}(function(e){return e.replace(/-([a-z])/g,(e=>e[1].toUpperCase()))}(e))}function c(e){let n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return n?["internationalizedArray",u(e),"Value"].join(""):["internationalizedArray",u(e)].join("")}const g={languages:[{id:"en",title:"English"},{id:"no",title:"Norsk"}]};function p(){return i.jsx(a.Card,{tone:"caution",border:!0,radius:2,padding:3,children:i.jsxs(a.Stack,{space:4,children:[i.jsxs(a.Text,{children:["An array of language objects must be passed into the ",i.jsx("code",{children:"internationalizedArray"})," ","helper function, each with an ",i.jsx("code",{children:"id"})," and ",i.jsx("code",{children:"title"})," field. Example:"]}),i.jsx(a.Card,{padding:2,border:!0,radius:2,children:i.jsx(a.Code,{size:1,language:"javascript",children:JSON.stringify(g,null,2)})})]})})}const y=d.default.createContext({languages:[]}),f=y.Provider;function h(e){const{members:t,value:s,schemaType:u,onChange:c}=e,g="boolean"==typeof u.readOnly&&u.readOnly,{options:y}=u,h=a.useToast(),[m,v]=d.default.useState(Array.isArray(y.languages)?y.languages:null);r.useEffect((()=>{m||Array.isArray(null==y?void 0:y.languages)||async function(){const e=Array.isArray(y.languages)?y.languages:await y.languages();v(e)}()}),[m,y]);const j=r.useCallback((e=>{if(!(null==m?void 0:m.length))return;const t={_type:"".concat(u.name,"Value")},i=e?[n(n({},t),{},{_key:e})]:m.filter((e=>!(null==s?void 0:s.length)||!s.find((n=>n._key===e.id)))).map((e=>n(n({},t),{},{_key:e.id}))),r=(null==s?void 0:s.length)?s.map((e=>e)):[],a=i.map((e=>{const n=m.findIndex((n=>e._key===n.id)),t=m.slice(n+1),i=r.findIndex((e=>t.find((n=>n.id===e._key))));return i<0?r.push(e):r.splice(i,0,e),i<0?l.insert([e],"after",[i]):l.insert([e],"before",[i])}));c([l.setIfMissing([]),...a])}),[m,c,u.name,s]),x=r.useCallback((()=>{if(!(null==s?void 0:s.length)||!(null==m?void 0:m.length))return;const e=s.reduce(((e,n)=>{const t=m.findIndex((e=>e.id===(null==n?void 0:n._key)));return t>-1&&(e[t]=n),e}),[]).filter(Boolean);(null==s?void 0:s.length)!==e.length&&h.push({title:"There was an error reordering languages",status:"warning"}),c(l.set(e))}),[h,m,c,s]),b=r.useMemo((()=>!(null==s?void 0:s.length)||!(null==m?void 0:m.length)||(null==s?void 0:s.every((e=>m.find((n=>(null==n?void 0:n.id)===(null==e?void 0:e._key))))))),[s,m]),k=r.useMemo((()=>m&&m.length>1?m.filter((e=>null==s?void 0:s.find((n=>n._key===e.id)))):[]),[m,s]),_=r.useMemo((()=>(null==s?void 0:s.length)&&k.length?s.map(((e,n)=>n===k.findIndex((n=>n.id===e._key))?null:e)).filter(Boolean):[]),[s,k]),O=r.useMemo((()=>!(null==m?void 0:m.length)||(null==m?void 0:m.length)&&m.every((e=>e.id&&e.title))),[m]);return r.useEffect((()=>{_.length>0&&b&&x()}),[_,b,x]),O?m?i.jsx(f,{value:{languages:m},children:i.jsxs(a.Stack,{space:2,children:[(null==t?void 0:t.length)>0?i.jsx(i.Fragment,{children:t.map((n=>"item"===n.kind?i.jsx(l.ArrayOfObjectsItem,{member:n,renderItem:e.renderItem,renderField:e.renderField,renderInput:e.renderInput,renderPreview:e.renderPreview},n.key):null))}):null,(null==m?void 0:m.length)>0&&k.length<m.length?i.jsxs(a.Stack,{space:2,children:[m.length>1?i.jsx(a.Grid,{columns:Math.min(m.length,5),gap:2,children:m.map((e=>i.jsx(a.Button,{tone:"primary",mode:"ghost",fontSize:1,disabled:g||Boolean(null==s?void 0:s.find((n=>n._key===e.id))),text:e.id.toUpperCase(),icon:o.AddIcon,onClick:()=>j(e.id)},e.id)))}):null,i.jsx(a.Button,{tone:"primary",mode:"ghost",disabled:g||s&&(null==s?void 0:s.length)>=(null==m?void 0:m.length),icon:o.AddIcon,text:(null==s?void 0:s.length)?"Add missing ".concat(m.length-s.length==1?"language":"languages"):1===m.length?"Add ".concat(m[0].title," Field"):"Add all languages",onClick:()=>j()})]}):null]})}):i.jsx(a.Spinner,{}):i.jsx(p,{})}var m=e=>{const{languages:t,type:i}=e,r="string"==typeof i?i:i.name,a=c(r),o=c(r,!0);return l.defineField({name:a,title:"Internationalized array",type:"array",components:{input:h},options:{languages:t},of:[l.defineField({name:o,type:o})],validation:e=>e.custom((async(e,t)=>{var l,i,r,a;if(!e)return!0;const o=Array.isArray(null==(i=null==(l=null==t?void 0:t.type)?void 0:l.options)?void 0:i.languages)?null==(r=null==t?void 0:t.type)?void 0:r.options.languages:await(null==(a=null==t?void 0:t.type)?void 0:a.options.languages());if(e&&e.length>o.length)return"Cannot be more than ".concat(1===o.length?"1 item":"".concat(o.length," items"));const s=(null==e?void 0:e.length)?e.filter((e=>!o.find((n=>e._key===n.id)))):[];if(s.length)return{message:"Array item keys must be valid languages registered to the field type",paths:s.map((e=>[{_key:e._key}]))};const d=(null==e?void 0:e.length)?e.filter((e=>Boolean(null==e?void 0:e._key))).reduce(((e,t)=>e[t._key]?n(n({},e),{},{[t._key]:[...e[t._key],t]}):n(n({},e),{},{[t._key]:[t]})),{}):{},u=Object.values(d).filter((e=>(null==e?void 0:e.length)>1)).flat();return!u.length||{message:"There can only be one field per language",paths:u.map((e=>[{_key:e._key}]))}}))})};function v(e){return"reference"===e.schemaType.name&&e.value?e.renderDefault(n(n({},e),{},{title:"",level:0})):e.children}function j(e){if(!(null==e?void 0:e.length))return;const n=e.map((e=>e.level));return n.includes("error")?"critical":n.includes("warning")?"caution":void 0}function x(e){const t=l.useFormValue(e.path.slice(0,-1)),s=n(n({},e.inputProps),{},{members:e.inputProps.members.filter((e=>"field"===e.kind&&"value"===e.name)),value:e.value}),{validation:u,value:c,onChange:g,readOnly:p}=s,{languages:f}=d.default.useContext(y),h=r.useMemo((()=>{var e;return null!=(e=null==t?void 0:t.map((e=>e._key)))?e:[]}),[t]),m=!!(null==f?void 0:f.length)&&f.find((e=>e.id===c._key)),v=r.useCallback((e=>{c&&(null==f?void 0:f.length)&&f.find((n=>n.id===e))&&g([l.set(e,["_key"])])}),[g,c,f]),x=r.useCallback((()=>{g(l.unset())}),[g]);return f?i.jsx(a.Card,{paddingTop:2,tone:j(u),children:i.jsxs(a.Stack,{space:2,children:[i.jsx(a.Card,{tone:"inherit",children:m?i.jsx(a.Label,{muted:!0,size:1,children:c._key}):i.jsx(a.MenuButton,{button:i.jsx(a.Button,{fontSize:1,text:'Change "'.concat(c._key,'"')}),id:"".concat(c._key,"-change-key"),menu:i.jsx(a.Menu,{children:f.map((e=>i.jsx(a.MenuItem,{disabled:h.includes(e.id),fontSize:1,text:e.id.toLocaleUpperCase(),onClick:()=>v(e.id)},e.id)))}),placement:"right",popover:{portal:!0}})}),i.jsxs(a.Flex,{align:"center",gap:2,children:[i.jsx(a.Card,{flex:1,tone:"inherit",children:e.inputProps.renderInput(e.inputProps)}),i.jsx(a.Card,{tone:"inherit",children:i.jsx(a.Button,{mode:"ghost",icon:o.RemoveIcon,tone:"critical",disabled:p,onClick:x})})]})]})}):i.jsx(a.Spinner,{})}var b=e=>{const{type:t}=e,i=c("string"==typeof t?t:t.name,!0);return l.defineField({name:i,title:"Internationalized array ".concat(t),type:"object",components:{item:x},fields:["string"==typeof t?l.defineField({name:"value",type:t,components:{field:v}}):n(n({},t),{},{name:"value",components:{field:v}})],preview:{select:{title:"value",subtitle:"_key"}}})};const k={languages:[],fieldTypes:[]},_=l.definePlugin((function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:k;const{languages:t,fieldTypes:l}=n(n({},k),e);return{name:"sanity-plugin-internationalized-array",schema:{types:[...l.map((e=>m({type:e,languages:t}))),...l.map((e=>b({type:e})))]}}}));exports.internationalizedArray=_;
1
+ "use strict";function e(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function n(n){for(var i=1;i<arguments.length;i++){var r=null!=arguments[i]?arguments[i]:{};i%2?e(Object(r),!0).forEach((function(e){t(n,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(n,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach((function(e){Object.defineProperty(n,e,Object.getOwnPropertyDescriptor(r,e))}))}return n}function t(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}Object.defineProperty(exports,"__esModule",{value:!0});var i=require("suspend-react"),r=require("react/jsx-runtime"),l=require("sanity"),a=require("react"),o=require("@sanity/ui"),s=require("@sanity/icons");function u(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function d(e){if(e&&e.__esModule)return e;var n=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var i=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,i.get?i:{enumerable:!0,get:function(){return e[t]}})}})),n.default=e,Object.freeze(n)}var c=d(i),g=u(a);const p="sanity-plugin-internationalized-array",y=()=>c.peek(["v0",p]);var f=a.memo((function(e){const n=l.useClient({apiVersion:e.apiVersion});var t;return Array.isArray(y())||(t=async()=>Array.isArray(e.languages)?e.languages:e.languages(n),c.preload((()=>t()),["v0",p])),null}));function h(e){return function(e){return e.split(" ").map((e=>e.charAt(0).toUpperCase()+e.slice(1))).join(" ")}(function(e){return e.replace(/-([a-z])/g,(e=>e[1].toUpperCase()))}(e))}function m(e){let n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return n?["internationalizedArray",h(e),"Value"].join(""):["internationalizedArray",h(e)].join("")}const v={languages:[{id:"en",title:"English"},{id:"no",title:"Norsk"}]};function j(){return r.jsx(o.Card,{tone:"caution",border:!0,radius:2,padding:3,children:r.jsxs(o.Stack,{space:4,children:[r.jsxs(o.Text,{children:["An array of language objects must be passed into the ",r.jsx("code",{children:"internationalizedArray"})," ","helper function, each with an ",r.jsx("code",{children:"id"})," and ",r.jsx("code",{children:"title"})," field. Example:"]}),r.jsx(o.Card,{padding:2,border:!0,radius:2,children:r.jsx(o.Code,{size:1,language:"javascript",children:JSON.stringify(v,null,2)})})]})})}const b=g.default.createContext({languages:[]}),x=b.Provider;function k(e){const{members:t,value:u,schemaType:d,onChange:c}=e,g="boolean"==typeof d.readOnly&&d.readOnly,{options:y}=d,f=o.useToast(),{apiVersion:h}=y,m=l.useClient({apiVersion:h}),v=Array.isArray(y.languages)?y.languages:i.suspend((async()=>"function"==typeof y.languages?y.languages(m):y.languages),["v0",p]),b=a.useCallback((e=>{if(!(null==v?void 0:v.length))return;const t={_type:"".concat(d.name,"Value")},i=e?[n(n({},t),{},{_key:e})]:v.filter((e=>!(null==u?void 0:u.length)||!u.find((n=>n._key===e.id)))).map((e=>n(n({},t),{},{_key:e.id}))),r=(null==u?void 0:u.length)?u.map((e=>e)):[],a=i.map((e=>{const n=v.findIndex((n=>e._key===n.id)),t=v.slice(n+1),i=r.findIndex((e=>t.find((n=>n.id===e._key))));return i<0?r.push(e):r.splice(i,0,e),i<0?l.insert([e],"after",[i]):l.insert([e],"before",[i])}));c([l.setIfMissing([]),...a])}),[v,c,d.name,u]),k=a.useCallback((()=>{if(!(null==u?void 0:u.length)||!(null==v?void 0:v.length))return;const e=u.reduce(((e,n)=>{const t=v.findIndex((e=>e.id===(null==n?void 0:n._key)));return t>-1&&(e[t]=n),e}),[]).filter(Boolean);(null==u?void 0:u.length)!==e.length&&f.push({title:"There was an error reordering languages",status:"warning"}),c(l.set(e))}),[f,v,c,u]),O=a.useMemo((()=>!(null==u?void 0:u.length)||!(null==v?void 0:v.length)||(null==u?void 0:u.every((e=>v.find((n=>(null==n?void 0:n.id)===(null==e?void 0:e._key))))))),[u,v]),_=a.useMemo((()=>v&&v.length>1?v.filter((e=>null==u?void 0:u.find((n=>n._key===e.id)))):[]),[v,u]),C=a.useMemo((()=>(null==u?void 0:u.length)&&_.length?u.map(((e,n)=>n===_.findIndex((n=>n.id===e._key))?null:e)).filter(Boolean):[]),[u,_]),A=a.useMemo((()=>!(null==v?void 0:v.length)||(null==v?void 0:v.length)&&v.every((e=>e.id&&e.title))),[v]);return a.useEffect((()=>{C.length>0&&O&&k()}),[C,O,k]),A?r.jsx(x,{value:{languages:v},children:r.jsxs(o.Stack,{space:2,children:[(null==t?void 0:t.length)>0?r.jsx(r.Fragment,{children:t.map((n=>"item"===n.kind?r.jsx(l.ArrayOfObjectsItem,{member:n,renderItem:e.renderItem,renderField:e.renderField,renderInput:e.renderInput,renderPreview:e.renderPreview},n.key):null))}):null,(null==v?void 0:v.length)>0&&_.length<v.length?r.jsxs(o.Stack,{space:2,children:[v.length>1?r.jsx(o.Grid,{columns:Math.min(v.length,5),gap:2,children:v.map((e=>r.jsx(o.Button,{tone:"primary",mode:"ghost",fontSize:1,disabled:g||Boolean(null==u?void 0:u.find((n=>n._key===e.id))),text:e.id.toUpperCase(),icon:s.AddIcon,onClick:()=>b(e.id)},e.id)))}):null,r.jsx(o.Button,{tone:"primary",mode:"ghost",disabled:g||u&&(null==u?void 0:u.length)>=(null==v?void 0:v.length),icon:s.AddIcon,text:(null==u?void 0:u.length)?"Add missing ".concat(v.length-u.length==1?"language":"languages"):1===v.length?"Add ".concat(v[0].title," Field"):"Add all languages",onClick:()=>b()})]}):null]})}):r.jsx(j,{})}var O=e=>{const{apiVersion:t,languages:i,type:r}=e,a="string"==typeof r?r:r.name,o=m(a),s=m(a,!0);return l.defineField({name:o,title:"Internationalized array",type:"array",components:{input:k},options:{apiVersion:t,languages:i},of:[l.defineField(n(n({},"string"==typeof r?{}:r),{},{name:s,type:s}))],validation:e=>e.custom((async(e,i)=>{var r,l,a;if(!e)return!0;const o=i.getClient({apiVersion:t}),s=Array.isArray(null==(l=null==(r=null==i?void 0:i.type)?void 0:r.options)?void 0:l.languages)?i.type.options.languages:Array.isArray(y())?y():await(null==(a=null==i?void 0:i.type)?void 0:a.options.languages(o));if(e&&e.length>s.length)return"Cannot be more than ".concat(1===s.length?"1 item":"".concat(s.length," items"));const u=(null==e?void 0:e.length)?e.filter((e=>!s.find((n=>e._key===n.id)))):[];if(u.length)return{message:"Array item keys must be valid languages registered to the field type",paths:u.map((e=>[{_key:e._key}]))};const d=(null==e?void 0:e.length)?e.filter((e=>Boolean(null==e?void 0:e._key))).reduce(((e,t)=>e[t._key]?n(n({},e),{},{[t._key]:[...e[t._key],t]}):n(n({},e),{},{[t._key]:[t]})),{}):{},c=Object.values(d).filter((e=>(null==e?void 0:e.length)>1)).flat();return!c.length||{message:"There can only be one field per language",paths:c.map((e=>[{_key:e._key}]))}}))})};function _(e){return"reference"===e.schemaType.name&&e.value?e.renderDefault(n(n({},e),{},{title:"",level:0})):e.children}function C(e){if(!(null==e?void 0:e.length))return;const n=e.map((e=>e.level));return n.includes("error")?"critical":n.includes("warning")?"caution":void 0}function A(e){const t=l.useFormValue(e.path.slice(0,-1)),i=n(n({},e.inputProps),{},{members:e.inputProps.members.filter((e=>"field"===e.kind&&"value"===e.name)),value:e.value}),{validation:u,value:d,onChange:c,readOnly:p}=i,{languages:y}=g.default.useContext(b),f=a.useMemo((()=>{var e;return null!=(e=null==t?void 0:t.map((e=>e._key)))?e:[]}),[t]),h=!!(null==y?void 0:y.length)&&y.find((e=>e.id===d._key)),m=a.useCallback((e=>{d&&(null==y?void 0:y.length)&&y.find((n=>n.id===e))&&c([l.set(e,["_key"])])}),[c,d,y]),v=a.useCallback((()=>{c(l.unset())}),[c]);return y?r.jsx(o.Card,{paddingTop:2,tone:C(u),children:r.jsxs(o.Stack,{space:2,children:[r.jsx(o.Card,{tone:"inherit",children:h?r.jsx(o.Label,{muted:!0,size:1,children:d._key}):r.jsx(o.MenuButton,{button:r.jsx(o.Button,{fontSize:1,text:'Change "'.concat(d._key,'"')}),id:"".concat(d._key,"-change-key"),menu:r.jsx(o.Menu,{children:y.map((e=>r.jsx(o.MenuItem,{disabled:f.includes(e.id),fontSize:1,text:e.id.toLocaleUpperCase(),onClick:()=>m(e.id)},e.id)))}),placement:"right",popover:{portal:!0}})}),r.jsxs(o.Flex,{align:"center",gap:2,children:[r.jsx(o.Card,{flex:1,tone:"inherit",children:e.inputProps.renderInput(e.inputProps)}),r.jsx(o.Card,{tone:"inherit",children:r.jsx(o.Button,{mode:"bleed",icon:s.RemoveCircleIcon,tone:"critical",disabled:p,onClick:v})})]})]})}):r.jsx(o.Spinner,{})}var P=e=>{const{type:t}=e,i=m("string"==typeof t?t:t.name,!0);return l.defineField({name:i,title:"Internationalized array ".concat(t),type:"object",components:{item:A},fields:["string"==typeof t?l.defineField({name:"value",type:t,components:{field:_}}):n(n({},t),{},{name:"value",components:{field:_}})],preview:{select:{title:"value",subtitle:"_key"}}})};const I={languages:[],fieldTypes:[]},w=l.definePlugin((function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:I;const{apiVersion:t="2022-11-27",languages:i,fieldTypes:l}=n(n({},I),e);return{name:"sanity-plugin-internationalized-array",studio:Array.isArray(i)?void 0:{components:{layout:e=>r.jsxs(r.Fragment,{children:[r.jsx(f,{apiVersion:t,languages:i}),e.renderDefault(e)]})}},schema:{types:[...l.map((e=>O({type:e,apiVersion:t,languages:i}))),...l.map((e=>P({type:e})))]}}}));exports.clear=()=>c.clear(["v0",p]),exports.internationalizedArray=w;
2
2
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/components/createFieldName.ts","../src/components/Feedback.tsx","../src/components/languageContext.tsx","../src/components/InternationalizedArray.tsx","../src/schema/array.ts","../src/components/InternationalizedField.tsx","../src/components/getToneFromValidation.ts","../src/components/InternationalizedInput.tsx","../src/schema/object.ts","../src/plugin.tsx"],"sourcesContent":["export function camelCase(string: string): string {\n return string.replace(/-([a-z])/g, (g) => g[1].toUpperCase())\n}\n\nexport function titleCase(string: string): string {\n return string\n .split(` `)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(` `)\n}\n\nexport function pascalCase(string: string): string {\n return titleCase(camelCase(string))\n}\n\nexport function createFieldName(name: string, addValue = false): string {\n return addValue\n ? [`internationalizedArray`, pascalCase(name), `Value`].join(``)\n : [`internationalizedArray`, pascalCase(name)].join(``)\n}\n","import {Text, Card, Stack, Code} from '@sanity/ui'\nimport React from 'react'\n\nconst schemaExample = {\n languages: [\n {id: 'en', title: 'English'},\n {id: 'no', title: 'Norsk'},\n ],\n}\n\nexport default function Feedback() {\n return (\n <Card tone=\"caution\" border radius={2} padding={3}>\n <Stack space={4}>\n <Text>\n An array of language objects must be passed into the <code>internationalizedArray</code>{' '}\n helper function, each with an <code>id</code> and <code>title</code> field. Example:\n </Text>\n <Card padding={2} border radius={2}>\n <Code size={1} language=\"javascript\">\n {JSON.stringify(schemaExample, null, 2)}\n </Code>\n </Card>\n </Stack>\n </Card>\n )\n}\n","import React from 'react'\n\nimport {Language} from '../types'\n\nexport const LanguageContext = React.createContext<{languages: Language[]}>({\n languages: [],\n})\n\nexport const LanguageProvider = LanguageContext.Provider\n","import React, {useCallback, useEffect, useMemo} from 'react'\nimport {\n insert,\n set,\n setIfMissing,\n ArrayOfObjectsItemMember,\n ArrayOfObjectsItem,\n ArrayOfObjectsInputProps,\n} from 'sanity'\nimport {Button, Grid, Spinner, Stack, useToast} from '@sanity/ui'\nimport {AddIcon} from '@sanity/icons'\n\nimport {Language, Value, ArraySchemaWithLanguageOptions} from '../types'\nimport Feedback from './Feedback'\n// TODO: Move this provider to the root component\nimport {LanguageProvider} from './languageContext'\n\nexport type InternationalizedArrayProps = ArrayOfObjectsInputProps<\n Value,\n ArraySchemaWithLanguageOptions\n>\n\nexport default function InternationalizedArray(props: InternationalizedArrayProps) {\n const {members, value, schemaType, onChange} = props\n const readOnly = typeof schemaType.readOnly === 'boolean' ? schemaType.readOnly : false\n const {options} = schemaType\n const toast = useToast()\n\n const [languages, setLanguages] = React.useState<Language[] | null>(\n Array.isArray(options.languages) ? options.languages : null\n )\n // Resolve async languages\n useEffect(() => {\n async function resolveLanguages() {\n const resolvedLanguages = Array.isArray(options.languages)\n ? options.languages\n : await options.languages()\n setLanguages(resolvedLanguages)\n }\n if (!languages && !Array.isArray(options?.languages)) {\n resolveLanguages()\n }\n }, [languages, options])\n\n const handleAddLanguage = useCallback(\n (languageId?: string) => {\n if (!languages?.length) {\n return\n }\n\n const itemBase = {_type: `${schemaType.name}Value`}\n\n // Create new items\n const newItems = languageId\n ? // Just one for this language\n [{...itemBase, _key: languageId}]\n : // Or one for every missing language\n languages\n .filter((language) =>\n value?.length ? !value.find((v) => v._key === language.id) : true\n )\n .map((language) => ({...itemBase, _key: language.id}))\n\n // Insert new items in the correct order\n const languagesInUse = value?.length ? value.map((v) => v) : []\n\n const insertions = newItems.map((item) => {\n // What's the original index of this language?\n const languageIndex = languages.findIndex((l) => item._key === l.id)\n\n // What languages are there beyond that index?\n const remainingLanguages = languages.slice(languageIndex + 1)\n\n // So what is the index in the current value array of the next language in the language array?\n const nextLanguageIndex = languagesInUse.findIndex((l) =>\n // eslint-disable-next-line max-nested-callbacks\n remainingLanguages.find((r) => r.id === l._key)\n )\n\n // Keep local state up to date incase multiple insertions are being made\n if (nextLanguageIndex < 0) {\n languagesInUse.push(item)\n } else {\n languagesInUse.splice(nextLanguageIndex, 0, item)\n }\n\n return nextLanguageIndex < 0\n ? // No next language (-1), add to end of array\n insert([item], 'after', [nextLanguageIndex])\n : // Next language found, insert before that\n insert([item], 'before', [nextLanguageIndex])\n })\n\n onChange([setIfMissing([]), ...insertions])\n },\n [languages, onChange, schemaType.name, value]\n )\n\n // TODO: This is lazy, reordering and re-setting the whole array – it could be surgical\n const handleRestoreOrder = useCallback(() => {\n if (!value?.length || !languages?.length) {\n return\n }\n\n // Create a new value array in the correct order\n // This would also strip out values that don't have a language as the key\n const updatedValue = value\n .reduce((acc, v) => {\n const newIndex = languages.findIndex((l) => l.id === v?._key)\n\n if (newIndex > -1) {\n acc[newIndex] = v\n }\n\n return acc\n }, [] as Value[])\n .filter(Boolean)\n\n if (value?.length !== updatedValue.length) {\n toast.push({\n title: 'There was an error reordering languages',\n status: 'warning',\n })\n }\n\n onChange(set(updatedValue))\n }, [toast, languages, onChange, value])\n\n const allKeysAreLanguages = useMemo(() => {\n if (!value?.length || !languages?.length) {\n return true\n }\n\n return value?.every((v) => languages.find((l) => l?.id === v?._key))\n }, [value, languages])\n\n // Check languages are in the correct order\n const languagesInUse = useMemo(\n () =>\n languages && languages.length > 1\n ? languages.filter((l) => value?.find((v) => v._key === l.id))\n : [],\n [languages, value]\n )\n\n const languagesOutOfOrder = useMemo(() => {\n if (!value?.length || !languagesInUse.length) {\n return []\n }\n\n return value\n .map((v, vIndex) => (vIndex === languagesInUse.findIndex((l) => l.id === v._key) ? null : v))\n .filter(Boolean)\n }, [value, languagesInUse])\n\n const languagesAreValid = useMemo(\n () =>\n !languages?.length || (languages?.length && languages.every((item) => item.id && item.title)),\n [languages]\n )\n\n useEffect(() => {\n if (languagesOutOfOrder.length > 0 && allKeysAreLanguages) {\n handleRestoreOrder()\n }\n }, [languagesOutOfOrder, allKeysAreLanguages, handleRestoreOrder])\n\n if (!languagesAreValid) {\n return <Feedback />\n }\n\n if (!languages) {\n return <Spinner />\n }\n\n return (\n <LanguageProvider value={{languages}}>\n <Stack space={2}>\n {members?.length > 0 ? (\n <>\n {/* TODO: Resolve type for ArrayOfObjectsItemMember */}\n {/* @ts-ignore */}\n {members.map((member: ArrayOfObjectsItemMember) => {\n if (member.kind === 'item') {\n return (\n <ArrayOfObjectsItem\n key={member.key}\n member={member}\n renderItem={props.renderItem}\n renderField={props.renderField}\n renderInput={props.renderInput}\n renderPreview={props.renderPreview}\n />\n )\n }\n\n return null\n })}\n </>\n ) : null}\n\n {/* This now happens automatically */}\n {/* {languagesOutOfOrder.length > 0 && allKeysAreLanguages ? (\n <Button\n tone=\"caution\"\n icon={RestoreIcon}\n onClick={() => handleRestoreOrder()}\n text=\"Restore order of languages\"\n />\n ) : null} */}\n\n {/* Show buttons if languages are configured */}\n {/* Hide them if all languages have values */}\n {languages?.length > 0 && languagesInUse.length < languages.length ? (\n <Stack space={2}>\n {/* Hide language-specific buttons if there's only one */}\n {/* No more than 5 columns */}\n {languages.length > 1 ? (\n <Grid columns={Math.min(languages.length, 5)} gap={2}>\n {languages.map((language) => (\n <Button\n key={language.id}\n tone=\"primary\"\n mode=\"ghost\"\n fontSize={1}\n disabled={readOnly || Boolean(value?.find((item) => item._key === language.id))}\n text={language.id.toUpperCase()}\n icon={AddIcon}\n onClick={() => handleAddLanguage(language.id)}\n />\n ))}\n </Grid>\n ) : null}\n <Button\n tone=\"primary\"\n mode=\"ghost\"\n disabled={readOnly || (value && value?.length >= languages?.length)}\n icon={AddIcon}\n text={\n // eslint-disable-next-line no-nested-ternary\n value?.length\n ? `Add missing ${\n languages.length - value.length === 1 ? `language` : `languages`\n }`\n : languages.length === 1\n ? `Add ${languages[0].title} Field`\n : `Add all languages`\n }\n onClick={() => handleAddLanguage()}\n />\n </Stack>\n ) : null}\n </Stack>\n </LanguageProvider>\n )\n}\n","import {defineField, FieldDefinition, Rule} from 'sanity'\n\nimport {createFieldName} from '../components/createFieldName'\nimport InternationalizedArray from '../components/InternationalizedArray'\nimport {Language, Value} from '../types'\n\ntype ArrayFactoryConfig = {\n languages: Language[] | (() => Promise<Language[]>)\n type: string | FieldDefinition\n}\n\nexport default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {\n const {languages, type} = config\n const typeName = typeof type === `string` ? type : type.name\n const arrayName = createFieldName(typeName)\n const objectName = createFieldName(typeName, true)\n\n return defineField({\n name: arrayName,\n title: 'Internationalized array',\n type: 'array',\n // TODO: Resolve this typing issue with the outer component\n // @ts-ignore\n components: {\n input: InternationalizedArray,\n },\n options: {languages},\n // TODO: Resolve this typing issue with the inner object\n // @ts-ignore\n of: [\n defineField({\n name: objectName,\n type: objectName,\n }),\n ],\n validation: (rule: Rule) =>\n rule.custom<Value[]>(async (value, context) => {\n if (!value) {\n return true\n }\n\n const contextLanguages: Language[] = Array.isArray(context?.type?.options?.languages)\n ? context?.type?.options.languages\n : await context?.type?.options.languages()\n\n if (value && value.length > contextLanguages.length) {\n return `Cannot be more than ${\n contextLanguages.length === 1 ? `1 item` : `${contextLanguages.length} items`\n }`\n }\n\n const nonLanguageKeys = value?.length\n ? value.filter((item) => !contextLanguages.find((language) => item._key === language.id))\n : []\n if (nonLanguageKeys.length) {\n return {\n message: `Array item keys must be valid languages registered to the field type`,\n paths: nonLanguageKeys.map((item) => [{_key: item._key}]),\n }\n }\n\n // Ensure there's no duplicate `language` fields\n type KeyedValues = {\n [key: string]: Value[]\n }\n\n const valuesByLanguage = value?.length\n ? value\n .filter((item) => Boolean(item?._key))\n .reduce((acc, cur) => {\n if (acc[cur._key]) {\n return {...acc, [cur._key]: [...acc[cur._key], cur]}\n }\n return {\n ...acc,\n [cur._key]: [cur],\n }\n }, {} as KeyedValues)\n : {}\n const duplicateValues = Object.values(valuesByLanguage)\n .filter((item) => item?.length > 1)\n .flat()\n if (duplicateValues.length) {\n return {\n message: 'There can only be one field per language',\n paths: duplicateValues.map((item) => [{_key: item._key}]),\n }\n }\n\n return true\n }),\n })\n}\n","import {FieldProps} from 'sanity'\n\nexport default function InternationalizedField(props: FieldProps) {\n // Show reference field selector if there's a value\n if (props.schemaType.name === 'reference' && props.value) {\n return props.renderDefault({\n ...props,\n title: '',\n level: 0,\n })\n }\n\n return props.children\n}\n","import {CardTone} from '@sanity/ui'\nimport {FormNodeValidation} from 'sanity'\n\nexport function getToneFromValidation(validations: FormNodeValidation[]): CardTone | undefined {\n if (!validations?.length) {\n return undefined\n }\n\n const validationLevels = validations.map((v) => v.level)\n\n if (validationLevels.includes('error')) {\n return `critical`\n } else if (validationLevels.includes('warning')) {\n return `caution`\n }\n\n return undefined\n}\n","import {ObjectItemProps, useFormValue} from 'sanity'\nimport React, {useCallback, useMemo} from 'react'\nimport {unset, set} from 'sanity'\nimport {Button, Flex, Label, MenuButton, Menu, MenuItem, Card, Spinner, Stack} from '@sanity/ui'\nimport {RemoveIcon} from '@sanity/icons'\n\nimport {getToneFromValidation} from './getToneFromValidation'\nimport {LanguageContext} from './languageContext'\n\ntype InternationalizedValue = {\n _type: string\n _key: string\n value: string\n}\n\nexport default function InternationalizedInput(props: ObjectItemProps<InternationalizedValue>) {\n const parentValue = useFormValue(props.path.slice(0, -1)) as InternationalizedValue[]\n\n const inlineProps = {\n ...props.inputProps,\n // This is the magic that makes inline editing work?\n members: props.inputProps.members.filter((m) => m.kind === 'field' && m.name === 'value'),\n // This just overrides the type\n // TODO: Remove this as it shouldn't be necessary?\n value: props.value as InternationalizedValue,\n }\n\n const {validation, value, onChange, readOnly} = inlineProps\n\n // The parent array contains the languages from the plugin config\n const {languages} = React.useContext(LanguageContext)\n\n const languageKeysInUse = useMemo(() => parentValue?.map((v) => v._key) ?? [], [parentValue])\n const keyIsValid = languages?.length ? languages.find((l) => l.id === value._key) : false\n\n // Changes the key of this item, ideally to a valid language\n const handleKeyChange = useCallback(\n (languageId: string) => {\n if (!value || !languages?.length || !languages.find((l) => l.id === languageId)) {\n return\n }\n\n onChange([set(languageId, ['_key'])])\n },\n [onChange, value, languages]\n )\n\n // Removes this item from the array\n const handleUnset = useCallback(() => {\n onChange(unset())\n }, [onChange])\n\n if (!languages) {\n return <Spinner />\n }\n\n return (\n <Card paddingTop={2} tone={getToneFromValidation(validation)}>\n <Stack space={2}>\n <Card tone=\"inherit\">\n {keyIsValid ? (\n <Label muted size={1}>\n {value._key}\n </Label>\n ) : (\n <MenuButton\n button={<Button fontSize={1} text={`Change \"${value._key}\"`} />}\n id={`${value._key}-change-key`}\n menu={\n <Menu>\n {languages.map((language) => (\n <MenuItem\n disabled={languageKeysInUse.includes(language.id)}\n fontSize={1}\n key={language.id}\n text={language.id.toLocaleUpperCase()}\n onClick={() => handleKeyChange(language.id)}\n />\n ))}\n </Menu>\n }\n placement=\"right\"\n popover={{portal: true}}\n />\n )}\n </Card>\n <Flex align=\"center\" gap={2}>\n <Card flex={1} tone=\"inherit\">\n {props.inputProps.renderInput(props.inputProps)}\n </Card>\n\n <Card tone=\"inherit\">\n <Button\n mode=\"ghost\"\n icon={RemoveIcon}\n tone=\"critical\"\n disabled={readOnly}\n onClick={handleUnset}\n />\n </Card>\n </Flex>\n </Stack>\n </Card>\n )\n}\n","import {defineField, FieldDefinition} from 'sanity'\n\nimport {createFieldName} from '../components/createFieldName'\nimport InternationalizedField from '../components/InternationalizedField'\nimport InternationalizedInput from '../components/InternationalizedInput'\n\ntype ObjectFactoryConfig = {\n type: string | FieldDefinition\n}\n\nexport default (config: ObjectFactoryConfig): FieldDefinition<'object'> => {\n const {type} = config\n const typeName = typeof type === `string` ? type : type.name\n const objectName = createFieldName(typeName, true)\n\n return defineField({\n name: objectName,\n title: `Internationalized array ${type}`,\n type: 'object',\n // TODO: Resolve this typing issue with the return type\n // @ts-ignore\n components: {\n // TODO: Resolve this typing issue with the outer component\n // @ts-ignore\n item: InternationalizedInput,\n },\n // TODO: Address this typing issue with the inner object\n // @ts-ignore\n fields: [\n typeof type === `string`\n ? // Define a simple field if all we have is the name as a string\n defineField({\n name: 'value',\n type,\n // TODO: Address this typing issue with components on a dynamic `type`\n // @ts-ignore\n components: {\n // TODO: Address this typing issue with the inner object\n // @ts-ignore\n field: InternationalizedField,\n },\n })\n : // Pass in the configured options, but overwrite the name\n {\n ...type,\n name: 'value',\n components: {\n field: InternationalizedField,\n },\n },\n ],\n preview: {\n select: {\n title: 'value',\n subtitle: '_key',\n },\n },\n })\n}\n","import {definePlugin} from 'sanity'\nimport {PluginConfig} from './types'\nimport array from './schema/array'\nimport object from './schema/object'\n\nconst CONFIG_DEFAULT = {\n languages: [],\n fieldTypes: [],\n}\n\nexport const internationalizedArray = definePlugin<PluginConfig>((config = CONFIG_DEFAULT) => {\n const {languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}\n\n return {\n name: 'sanity-plugin-internationalized-array',\n schema: {\n types: [\n ...fieldTypes.map((type) => array({type, languages})),\n ...fieldTypes.map((type) => object({type})),\n ],\n },\n }\n})\n"],"names":["pascalCase","string","split","map","word","charAt","toUpperCase","slice","join","titleCase","replace","g","camelCase","createFieldName","name","addValue","schemaExample","languages","id","title","Feedback","jsx","Card","tone","border","radius","padding","children","jsxs","Stack","space","Text","Code","size","language","JSON","stringify","LanguageContext","React","createContext","LanguageProvider","Provider","InternationalizedArray","props","members","value","schemaType","onChange","readOnly","options","toast","useToast","setLanguages","useState","Array","isArray","useEffect","async","resolvedLanguages","resolveLanguages","handleAddLanguage","useCallback","languageId","length","itemBase","_type","concat","newItems","_key","filter","find","v","languagesInUse","insertions","item","languageIndex","findIndex","l","remainingLanguages","nextLanguageIndex","r","push","splice","insert","setIfMissing","handleRestoreOrder","updatedValue","reduce","acc","newIndex","Boolean","status","set","allKeysAreLanguages","useMemo","every","languagesOutOfOrder","vIndex","languagesAreValid","Fragment","member","kind","ArrayOfObjectsItem","renderItem","renderField","renderInput","renderPreview","key","Grid","columns","Math","min","gap","Button","mode","fontSize","disabled","text","icon","AddIcon","onClick","Spinner","array","config","type","typeName","arrayName","objectName","defineField","components","input","of","validation","rule","custom","context","_a","_b","_c","_d","contextLanguages","nonLanguageKeys","message","paths","valuesByLanguage","cur","_objectSpread","duplicateValues","Object","values","flat","InternationalizedField","renderDefault","level","getToneFromValidation","validations","validationLevels","includes","InternationalizedInput","parentValue","useFormValue","path","inlineProps","inputProps","m","useContext","languageKeysInUse","keyIsValid","handleKeyChange","handleUnset","unset","paddingTop","Label","muted","MenuButton","button","menu","Menu","MenuItem","toLocaleUpperCase","placement","popover","portal","Flex","align","flex","RemoveIcon","object","fields","field","preview","select","subtitle","CONFIG_DEFAULT","fieldTypes","internationalizedArray","definePlugin","schema","types"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/cache.ts","../src/components/Preload.tsx","../src/components/createFieldName.ts","../src/components/Feedback.tsx","../src/components/languageContext.tsx","../src/components/InternationalizedArray.tsx","../src/schema/array.ts","../src/components/InternationalizedField.tsx","../src/components/getToneFromValidation.ts","../src/components/InternationalizedInput.tsx","../src/schema/object.ts","../src/plugin.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\n\nimport * as suspend from 'suspend-react'\nimport type {Language} from './types'\n\nexport const namespace = 'sanity-plugin-internationalized-array'\n\nexport const version = 'v0'\n\n// https://github.com/pmndrs/suspend-react#preloading\nexport const preload = (fn: () => Promise<Language[]>) =>\n suspend.preload(() => fn(), [version, namespace])\n\n// https://github.com/pmndrs/suspend-react#cache-busting\nexport const clear = () => suspend.clear([version, namespace])\n\n// https://github.com/pmndrs/suspend-react#peeking-into-entries-outside-of-suspense\nexport const peek = () => suspend.peek([version, namespace]) as Language[] | undefined\n","import {peek, preload} from '../cache'\nimport {memo} from 'react'\nimport type {PluginConfig} from '../types'\nimport {useClient} from 'sanity'\n\nexport default memo(function Preload(\n props: Required<Pick<PluginConfig, 'apiVersion' | 'languages'>>\n) {\n const client = useClient({apiVersion: props.apiVersion})\n if (!Array.isArray(peek())) {\n // eslint-disable-next-line require-await\n preload(async () =>\n Array.isArray(props.languages) ? props.languages : props.languages(client)\n )\n }\n\n return null\n})\n","export function camelCase(string: string): string {\n return string.replace(/-([a-z])/g, (g) => g[1].toUpperCase())\n}\n\nexport function titleCase(string: string): string {\n return string\n .split(` `)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(` `)\n}\n\nexport function pascalCase(string: string): string {\n return titleCase(camelCase(string))\n}\n\nexport function createFieldName(name: string, addValue = false): string {\n return addValue\n ? [`internationalizedArray`, pascalCase(name), `Value`].join(``)\n : [`internationalizedArray`, pascalCase(name)].join(``)\n}\n","import {Text, Card, Stack, Code} from '@sanity/ui'\nimport React from 'react'\n\nconst schemaExample = {\n languages: [\n {id: 'en', title: 'English'},\n {id: 'no', title: 'Norsk'},\n ],\n}\n\nexport default function Feedback() {\n return (\n <Card tone=\"caution\" border radius={2} padding={3}>\n <Stack space={4}>\n <Text>\n An array of language objects must be passed into the <code>internationalizedArray</code>{' '}\n helper function, each with an <code>id</code> and <code>title</code> field. Example:\n </Text>\n <Card padding={2} border radius={2}>\n <Code size={1} language=\"javascript\">\n {JSON.stringify(schemaExample, null, 2)}\n </Code>\n </Card>\n </Stack>\n </Card>\n )\n}\n","import React from 'react'\n\nimport {Language} from '../types'\n\nexport const LanguageContext = React.createContext<{languages: Language[]}>({\n languages: [],\n})\n\nexport const LanguageProvider = LanguageContext.Provider\n","import React, {Fragment, useCallback, useEffect, useMemo} from 'react'\nimport {\n insert,\n set,\n setIfMissing,\n ArrayOfObjectsItemMember,\n ArrayOfObjectsItem,\n ArrayOfObjectsInputProps,\n useClient,\n} from 'sanity'\nimport {Button, Grid, Stack, useToast} from '@sanity/ui'\nimport {AddIcon} from '@sanity/icons'\nimport {suspend} from 'suspend-react'\n\nimport type {Value, ArraySchemaWithLanguageOptions} from '../types'\nimport Feedback from './Feedback'\n// TODO: Move this provider to the root component\nimport {LanguageProvider} from './languageContext'\nimport {namespace, version} from '../cache'\n\nexport type InternationalizedArrayProps = ArrayOfObjectsInputProps<\n Value,\n ArraySchemaWithLanguageOptions\n>\n\nexport default function InternationalizedArray(props: InternationalizedArrayProps) {\n const {members, value, schemaType, onChange} = props\n const readOnly = typeof schemaType.readOnly === 'boolean' ? schemaType.readOnly : false\n const {options} = schemaType\n const toast = useToast()\n\n const {apiVersion} = options\n const client = useClient({apiVersion})\n const languages = Array.isArray(options.languages)\n ? options.languages\n : // eslint-disable-next-line require-await\n suspend(async () => {\n if (typeof options.languages === 'function') {\n return options.languages(client)\n }\n return options.languages\n }, [version, namespace])\n\n const handleAddLanguage = useCallback(\n (languageId?: string) => {\n if (!languages?.length) {\n return\n }\n\n const itemBase = {_type: `${schemaType.name}Value`}\n\n // Create new items\n const newItems = languageId\n ? // Just one for this language\n [{...itemBase, _key: languageId}]\n : // Or one for every missing language\n languages\n .filter((language) =>\n value?.length ? !value.find((v) => v._key === language.id) : true\n )\n .map((language) => ({...itemBase, _key: language.id}))\n\n // Insert new items in the correct order\n const languagesInUse = value?.length ? value.map((v) => v) : []\n\n const insertions = newItems.map((item) => {\n // What's the original index of this language?\n const languageIndex = languages.findIndex((l) => item._key === l.id)\n\n // What languages are there beyond that index?\n const remainingLanguages = languages.slice(languageIndex + 1)\n\n // So what is the index in the current value array of the next language in the language array?\n const nextLanguageIndex = languagesInUse.findIndex((l) =>\n // eslint-disable-next-line max-nested-callbacks\n remainingLanguages.find((r) => r.id === l._key)\n )\n\n // Keep local state up to date incase multiple insertions are being made\n if (nextLanguageIndex < 0) {\n languagesInUse.push(item)\n } else {\n languagesInUse.splice(nextLanguageIndex, 0, item)\n }\n\n return nextLanguageIndex < 0\n ? // No next language (-1), add to end of array\n insert([item], 'after', [nextLanguageIndex])\n : // Next language found, insert before that\n insert([item], 'before', [nextLanguageIndex])\n })\n\n onChange([setIfMissing([]), ...insertions])\n },\n [languages, onChange, schemaType.name, value]\n )\n\n // TODO: This is lazy, reordering and re-setting the whole array – it could be surgical\n const handleRestoreOrder = useCallback(() => {\n if (!value?.length || !languages?.length) {\n return\n }\n\n // Create a new value array in the correct order\n // This would also strip out values that don't have a language as the key\n const updatedValue = value\n .reduce((acc, v) => {\n const newIndex = languages.findIndex((l) => l.id === v?._key)\n\n if (newIndex > -1) {\n acc[newIndex] = v\n }\n\n return acc\n }, [] as Value[])\n .filter(Boolean)\n\n if (value?.length !== updatedValue.length) {\n toast.push({\n title: 'There was an error reordering languages',\n status: 'warning',\n })\n }\n\n onChange(set(updatedValue))\n }, [toast, languages, onChange, value])\n\n const allKeysAreLanguages = useMemo(() => {\n if (!value?.length || !languages?.length) {\n return true\n }\n\n return value?.every((v) => languages.find((l) => l?.id === v?._key))\n }, [value, languages])\n\n // Check languages are in the correct order\n const languagesInUse = useMemo(\n () =>\n languages && languages.length > 1\n ? languages.filter((l) => value?.find((v) => v._key === l.id))\n : [],\n [languages, value]\n )\n\n const languagesOutOfOrder = useMemo(() => {\n if (!value?.length || !languagesInUse.length) {\n return []\n }\n\n return value\n .map((v, vIndex) => (vIndex === languagesInUse.findIndex((l) => l.id === v._key) ? null : v))\n .filter(Boolean)\n }, [value, languagesInUse])\n\n const languagesAreValid = useMemo(\n () =>\n !languages?.length || (languages?.length && languages.every((item) => item.id && item.title)),\n [languages]\n )\n\n useEffect(() => {\n if (languagesOutOfOrder.length > 0 && allKeysAreLanguages) {\n handleRestoreOrder()\n }\n }, [languagesOutOfOrder, allKeysAreLanguages, handleRestoreOrder])\n\n if (!languagesAreValid) {\n return <Feedback />\n }\n\n return (\n <LanguageProvider value={{languages}}>\n <Stack space={2}>\n {members?.length > 0 ? (\n <>\n {/* TODO: Resolve type for ArrayOfObjectsItemMember */}\n {/* @ts-ignore */}\n {members.map((member: ArrayOfObjectsItemMember) => {\n if (member.kind === 'item') {\n return (\n <ArrayOfObjectsItem\n key={member.key}\n member={member}\n renderItem={props.renderItem}\n renderField={props.renderField}\n renderInput={props.renderInput}\n renderPreview={props.renderPreview}\n />\n )\n }\n\n return null\n })}\n </>\n ) : null}\n\n {/* This now happens automatically */}\n {/* {languagesOutOfOrder.length > 0 && allKeysAreLanguages ? (\n <Button\n tone=\"caution\"\n icon={RestoreIcon}\n onClick={() => handleRestoreOrder()}\n text=\"Restore order of languages\"\n />\n ) : null} */}\n\n {/* Show buttons if languages are configured */}\n {/* Hide them if all languages have values */}\n {languages?.length > 0 && languagesInUse.length < languages.length ? (\n <Stack space={2}>\n {/* Hide language-specific buttons if there's only one */}\n {/* No more than 5 columns */}\n {languages.length > 1 ? (\n <Grid columns={Math.min(languages.length, 5)} gap={2}>\n {languages.map((language) => (\n <Button\n key={language.id}\n tone=\"primary\"\n mode=\"ghost\"\n fontSize={1}\n disabled={readOnly || Boolean(value?.find((item) => item._key === language.id))}\n text={language.id.toUpperCase()}\n icon={AddIcon}\n onClick={() => handleAddLanguage(language.id)}\n />\n ))}\n </Grid>\n ) : null}\n <Button\n tone=\"primary\"\n mode=\"ghost\"\n disabled={readOnly || (value && value?.length >= languages?.length)}\n icon={AddIcon}\n text={\n // eslint-disable-next-line no-nested-ternary\n value?.length\n ? `Add missing ${\n languages.length - value.length === 1 ? `language` : `languages`\n }`\n : languages.length === 1\n ? `Add ${languages[0].title} Field`\n : `Add all languages`\n }\n onClick={() => handleAddLanguage()}\n />\n </Stack>\n ) : null}\n </Stack>\n </LanguageProvider>\n )\n}\n","/* eslint-disable no-nested-ternary */\nimport {defineField, type FieldDefinition, type Rule, type SanityClient} from 'sanity'\nimport {peek} from '../cache'\n\nimport {createFieldName} from '../components/createFieldName'\nimport InternationalizedArray from '../components/InternationalizedArray'\nimport {Language, Value} from '../types'\n\ntype ArrayFactoryConfig = {\n apiVersion: string\n languages: Language[] | ((client: SanityClient) => Promise<Language[]>)\n type: string | FieldDefinition\n}\n\nexport default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {\n const {apiVersion, languages, type} = config\n const typeName = typeof type === `string` ? type : type.name\n const arrayName = createFieldName(typeName)\n const objectName = createFieldName(typeName, true)\n\n return defineField({\n name: arrayName,\n title: 'Internationalized array',\n type: 'array',\n // TODO: Resolve this typing issue with the outer component\n // @ts-ignore\n components: {\n input: InternationalizedArray,\n },\n options: {apiVersion, languages},\n // TODO: Resolve this typing issue with the inner object\n // @ts-ignore\n of: [\n defineField({\n ...(typeof type === 'string' ? {} : type),\n name: objectName,\n type: objectName,\n }),\n ],\n validation: (rule: Rule) =>\n rule.custom<Value[]>(async (value, context) => {\n if (!value) {\n return true\n }\n\n const client = context.getClient({apiVersion})\n const contextLanguages: Language[] = Array.isArray(context?.type?.options?.languages)\n ? context!.type!.options.languages\n : Array.isArray(peek())\n ? peek()\n : await context?.type?.options.languages(client)\n\n if (value && value.length > contextLanguages.length) {\n return `Cannot be more than ${\n contextLanguages.length === 1 ? `1 item` : `${contextLanguages.length} items`\n }`\n }\n\n const nonLanguageKeys = value?.length\n ? value.filter((item) => !contextLanguages.find((language) => item._key === language.id))\n : []\n if (nonLanguageKeys.length) {\n return {\n message: `Array item keys must be valid languages registered to the field type`,\n paths: nonLanguageKeys.map((item) => [{_key: item._key}]),\n }\n }\n\n // Ensure there's no duplicate `language` fields\n type KeyedValues = {\n [key: string]: Value[]\n }\n\n const valuesByLanguage = value?.length\n ? value\n .filter((item) => Boolean(item?._key))\n .reduce((acc, cur) => {\n if (acc[cur._key]) {\n return {...acc, [cur._key]: [...acc[cur._key], cur]}\n }\n return {\n ...acc,\n [cur._key]: [cur],\n }\n }, {} as KeyedValues)\n : {}\n const duplicateValues = Object.values(valuesByLanguage)\n .filter((item) => item?.length > 1)\n .flat()\n if (duplicateValues.length) {\n return {\n message: 'There can only be one field per language',\n paths: duplicateValues.map((item) => [{_key: item._key}]),\n }\n }\n\n return true\n }),\n })\n}\n","import {FieldProps} from 'sanity'\n\nexport default function InternationalizedField(props: FieldProps) {\n // Show reference field selector if there's a value\n if (props.schemaType.name === 'reference' && props.value) {\n return props.renderDefault({\n ...props,\n title: '',\n level: 0,\n })\n }\n\n return props.children\n}\n","import {CardTone} from '@sanity/ui'\nimport {FormNodeValidation} from 'sanity'\n\nexport function getToneFromValidation(validations: FormNodeValidation[]): CardTone | undefined {\n if (!validations?.length) {\n return undefined\n }\n\n const validationLevels = validations.map((v) => v.level)\n\n if (validationLevels.includes('error')) {\n return `critical`\n } else if (validationLevels.includes('warning')) {\n return `caution`\n }\n\n return undefined\n}\n","import {ObjectItemProps, useFormValue} from 'sanity'\nimport React, {useCallback, useMemo} from 'react'\nimport {unset, set} from 'sanity'\nimport {Button, Flex, Label, MenuButton, Menu, MenuItem, Card, Spinner, Stack} from '@sanity/ui'\nimport {RemoveCircleIcon} from '@sanity/icons'\n\nimport {getToneFromValidation} from './getToneFromValidation'\nimport {LanguageContext} from './languageContext'\n\ntype InternationalizedValue = {\n _type: string\n _key: string\n value: string\n}\n\nexport default function InternationalizedInput(props: ObjectItemProps<InternationalizedValue>) {\n const parentValue = useFormValue(props.path.slice(0, -1)) as InternationalizedValue[]\n\n const inlineProps = {\n ...props.inputProps,\n // This is the magic that makes inline editing work?\n members: props.inputProps.members.filter((m) => m.kind === 'field' && m.name === 'value'),\n // This just overrides the type\n // TODO: Remove this as it shouldn't be necessary?\n value: props.value as InternationalizedValue,\n }\n\n const {validation, value, onChange, readOnly} = inlineProps\n\n // The parent array contains the languages from the plugin config\n const {languages} = React.useContext(LanguageContext)\n\n const languageKeysInUse = useMemo(() => parentValue?.map((v) => v._key) ?? [], [parentValue])\n const keyIsValid = languages?.length ? languages.find((l) => l.id === value._key) : false\n\n // Changes the key of this item, ideally to a valid language\n const handleKeyChange = useCallback(\n (languageId: string) => {\n if (!value || !languages?.length || !languages.find((l) => l.id === languageId)) {\n return\n }\n\n onChange([set(languageId, ['_key'])])\n },\n [onChange, value, languages]\n )\n\n // Removes this item from the array\n const handleUnset = useCallback(() => {\n onChange(unset())\n }, [onChange])\n\n if (!languages) {\n return <Spinner />\n }\n\n return (\n <Card paddingTop={2} tone={getToneFromValidation(validation)}>\n <Stack space={2}>\n <Card tone=\"inherit\">\n {keyIsValid ? (\n <Label muted size={1}>\n {value._key}\n </Label>\n ) : (\n <MenuButton\n button={<Button fontSize={1} text={`Change \"${value._key}\"`} />}\n id={`${value._key}-change-key`}\n menu={\n <Menu>\n {languages.map((language) => (\n <MenuItem\n disabled={languageKeysInUse.includes(language.id)}\n fontSize={1}\n key={language.id}\n text={language.id.toLocaleUpperCase()}\n onClick={() => handleKeyChange(language.id)}\n />\n ))}\n </Menu>\n }\n placement=\"right\"\n popover={{portal: true}}\n />\n )}\n </Card>\n <Flex align=\"center\" gap={2}>\n <Card flex={1} tone=\"inherit\">\n {props.inputProps.renderInput(props.inputProps)}\n </Card>\n\n <Card tone=\"inherit\">\n <Button\n mode=\"bleed\"\n icon={RemoveCircleIcon}\n tone=\"critical\"\n disabled={readOnly}\n onClick={handleUnset}\n />\n </Card>\n </Flex>\n </Stack>\n </Card>\n )\n}\n","import {defineField, FieldDefinition} from 'sanity'\n\nimport {createFieldName} from '../components/createFieldName'\nimport InternationalizedField from '../components/InternationalizedField'\nimport InternationalizedInput from '../components/InternationalizedInput'\n\ntype ObjectFactoryConfig = {\n type: string | FieldDefinition\n}\n\nexport default (config: ObjectFactoryConfig): FieldDefinition<'object'> => {\n const {type} = config\n const typeName = typeof type === `string` ? type : type.name\n const objectName = createFieldName(typeName, true)\n\n return defineField({\n name: objectName,\n title: `Internationalized array ${type}`,\n type: 'object',\n // TODO: Resolve this typing issue with the return type\n // @ts-ignore\n components: {\n // TODO: Resolve this typing issue with the outer component\n // @ts-ignore\n item: InternationalizedInput,\n },\n // TODO: Address this typing issue with the inner object\n // @ts-ignore\n fields: [\n typeof type === `string`\n ? // Define a simple field if all we have is the name as a string\n defineField({\n name: 'value',\n type,\n // TODO: Address this typing issue with components on a dynamic `type`\n // @ts-ignore\n components: {\n // TODO: Address this typing issue with the inner object\n // @ts-ignore\n field: InternationalizedField,\n },\n })\n : // Pass in the configured options, but overwrite the name\n {\n ...type,\n name: 'value',\n components: {\n field: InternationalizedField,\n },\n },\n ],\n preview: {\n select: {\n title: 'value',\n subtitle: '_key',\n },\n },\n })\n}\n","import React from 'react'\nimport {definePlugin} from 'sanity'\nimport Preload from './components/Preload'\nimport array from './schema/array'\nimport object from './schema/object'\nimport {PluginConfig} from './types'\n\nconst CONFIG_DEFAULT: PluginConfig = {\n languages: [],\n fieldTypes: [],\n}\n\nexport const internationalizedArray = definePlugin<PluginConfig>((config = CONFIG_DEFAULT) => {\n const {apiVersion = '2022-11-27', languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}\n\n return {\n name: 'sanity-plugin-internationalized-array',\n // If `languages` is a callback then let's preload it\n studio: Array.isArray(languages)\n ? undefined\n : {\n components: {\n layout: (props) => (\n <>\n <Preload apiVersion={apiVersion} languages={languages} />\n {props.renderDefault(props)}\n </>\n ),\n },\n },\n schema: {\n types: [\n ...fieldTypes.map((type) => array({type, apiVersion, languages})),\n ...fieldTypes.map((type) => object({type})),\n ],\n },\n }\n})\n"],"names":["namespace","peek","suspend","Preload","memo","props","client","useClient","apiVersion","fn","Array","isArray","async","languages","preload","pascalCase","string","split","map","word","charAt","toUpperCase","slice","join","titleCase","replace","g","camelCase","createFieldName","name","addValue","schemaExample","id","title","Feedback","jsx","Card","tone","border","radius","padding","children","jsxs","Stack","space","Text","Code","size","language","JSON","stringify","LanguageContext","React","createContext","LanguageProvider","Provider","InternationalizedArray","members","value","schemaType","onChange","readOnly","options","toast","useToast","handleAddLanguage","useCallback","languageId","length","itemBase","_type","concat","newItems","_key","filter","find","v","languagesInUse","insertions","item","languageIndex","findIndex","l","remainingLanguages","nextLanguageIndex","r","push","splice","insert","setIfMissing","handleRestoreOrder","updatedValue","reduce","acc","newIndex","Boolean","status","set","allKeysAreLanguages","useMemo","every","languagesOutOfOrder","vIndex","languagesAreValid","useEffect","Fragment","member","kind","ArrayOfObjectsItem","renderItem","renderField","renderInput","renderPreview","key","Grid","columns","Math","min","gap","Button","mode","fontSize","disabled","text","icon","AddIcon","onClick","array","config","type","typeName","arrayName","objectName","defineField","components","input","of","_objectSpread","validation","rule","custom","context","_a","_b","_c","getClient","contextLanguages","nonLanguageKeys","message","paths","valuesByLanguage","cur","duplicateValues","Object","values","flat","InternationalizedField","renderDefault","level","getToneFromValidation","validations","validationLevels","includes","InternationalizedInput","parentValue","useFormValue","path","inlineProps","inputProps","m","useContext","languageKeysInUse","keyIsValid","handleKeyChange","handleUnset","unset","paddingTop","Label","muted","MenuButton","button","menu","Menu","MenuItem","toLocaleUpperCase","placement","popover","portal","Flex","align","flex","RemoveCircleIcon","Spinner","object","fields","field","preview","select","subtitle","CONFIG_DEFAULT","fieldTypes","internationalizedArray","definePlugin","studio","layout","schema","types","clear"],"mappings":"4wCAKO,MAAMA,EAAY,wCAYZC,EAAO,IAAMC,EAAQD,KAAK,CAVhB,KAU0BD,ICZjD,IAAAG,EAAeC,QAAK,SAClBC,GAEA,MAAMC,EAASC,EAAAA,UAAU,CAACC,WAAYH,EAAMG,aDEtBC,MCMf,OAPFC,MAAMC,QAAQV,ODCGQ,ECCZG,SACNF,MAAMC,QAAQN,EAAMQ,WAAaR,EAAMQ,UAAYR,EAAMQ,UAAUP,GDDvEJ,EAAQY,SAAQ,IAAML,KAAM,CAJP,KAIiBT,KCK/B,IACT,ICNO,SAASe,EAAWC,GAClB,OARF,SAAmBA,GACxB,OAAOA,EACJC,MAAM,KACNC,KAAKC,GAASA,EAAKC,OAAO,GAAGC,cAAgBF,EAAKG,MAAM,KACxDC,KAAQ,IACb,CAGSC,CAZF,SAAmBR,GACjB,OAAAA,EAAOS,QAAQ,aAAcC,GAAMA,EAAE,GAAGL,eACjD,CAUmBM,CAAUX,GAC7B,CAEgB,SAAAY,EAAgBC,GAAwC,IAA1BC,0DAC5C,OAAOA,EACH,CAA2Bf,yBAAAA,EAAWc,YAAgBN,SACtD,CAAA,yBAA2BR,EAAWc,IAAON,KAAO,GAC1D,CChBA,MAAMQ,EAAgB,CACpBlB,UAAW,CACT,CAACmB,GAAI,KAAMC,MAAO,WAClB,CAACD,GAAI,KAAMC,MAAO,WAItB,SAAwBC,IACtB,OACGC,EAAAA,IAAAC,EAAAA,KAAA,CAAKC,KAAK,UAAUC,QAAM,EAACC,OAAQ,EAAGC,QAAS,EAC9CC,SAACC,EAAAA,KAAAC,QAAA,CAAMC,MAAO,EACZH,SAAA,CAACC,EAAAA,KAAAG,EAAAA,KAAA,CAAKJ,SAAA,CAAA,wDACkDN,EAAAA,IAAA,OAAA,CAAKM,SAAA,2BAA8B,IAAI,iCAC9DN,EAAAA,IAAA,OAAA,CAAKM,SAAA,OAAS,QAAMN,EAAAA,IAAA,OAAA,CAAKM,SAAA,UAAY,sBAErEN,EAAAA,IAAAC,EAAAA,KAAA,CAAKI,QAAS,EAAGF,QAAM,EAACC,OAAQ,EAC/BE,SAACN,EAAAA,IAAAW,OAAA,CAAKC,KAAM,EAAGC,SAAS,aACrBP,SAAKQ,KAAAC,UAAUnB,EAAe,KAAM,WAMjD,CCtBa,MAAAoB,EAAkBC,UAAMC,cAAuC,CAC1ExC,UAAW,KAGAyC,EAAmBH,EAAgBI,SCiBhD,SAAwBC,EAAuBnD,GAC7C,MAAMoD,QAACA,EAAAC,MAASA,EAAOC,WAAAA,EAAAC,SAAYA,GAAYvD,EACzCwD,EAA0C,kBAAxBF,EAAWE,UAAyBF,EAAWE,UACjEC,QAACA,GAAWH,EACZI,EAAQC,EAAAA,YAERxD,WAACA,GAAcsD,EACfxD,EAASC,EAAAA,UAAU,CAACC,eACpBK,EAAYH,MAAMC,QAAQmD,EAAQjD,WACpCiD,EAAQjD,UAERX,EAAAA,SAAQU,SAC2B,mBAAtBkD,EAAQjD,UACViD,EAAQjD,UAAUP,GAEpBwD,EAAQjD,WACd,CLlCc,KKkCJb,IAEXiE,EAAoBC,EAAAA,aACvBC,IACK,WAACtD,WAAWuD,QACd,OAGF,MAAMC,EAAW,CAACC,MAAO,GAAAC,OAAGZ,EAAW9B,KAAW,UAG5C2C,EAAWL,EAEb,QAAKE,OAAUI,KAAMN,KAErBtD,EACG6D,QAAQ1B,KACA,MAAPU,OAAO,EAAAA,EAAAU,UAAUV,EAAMiB,MAAMC,GAAMA,EAAEH,OAASzB,EAAShB,OAExDd,KAAK8B,UAAkBqB,GAAU,CAAA,EAAA,CAAAI,KAAMzB,EAAShB,OAGjD6C,SAAiBnB,WAAOU,QAASV,EAAMxC,KAAK0D,GAAMA,IAAK,GAEvDE,EAAaN,EAAStD,KAAK6D,IAEzB,MAAAC,EAAgBnE,EAAUoE,WAAWC,GAAMH,EAAKN,OAASS,EAAElD,KAG3DmD,EAAqBtE,EAAUS,MAAM0D,EAAgB,GAGrDI,EAAoBP,EAAeI,WAAWC,GAElDC,EAAmBR,MAAMU,GAAMA,EAAErD,KAAOkD,EAAET,SAU5C,OANIW,EAAoB,EACtBP,EAAeS,KAAKP,GAEpBF,EAAeU,OAAOH,EAAmB,EAAGL,GAGvCK,EAAoB,EAEvBI,EAAAA,OAAO,CAACT,GAAO,QAAS,CAACK,IAEzBI,EAAAA,OAAO,CAACT,GAAO,SAAU,CAACK,GAAkB,IAGlDxB,EAAS,CAAC6B,EAAAA,aAAa,OAAQX,GAAW,GAE5C,CAACjE,EAAW+C,EAAUD,EAAW9B,KAAM6B,IAInCgC,EAAqBxB,EAAAA,aAAY,KACrC,KAAK,MAAAR,OAAA,EAAAA,EAAOU,iBAAWvD,WAAWuD,QAChC,OAKF,MAAMuB,EAAejC,EAClBkC,QAAO,CAACC,EAAKjB,KACN,MAAAkB,EAAWjF,EAAUoE,WAAWC,GAAMA,EAAElD,YAAO4C,WAAGH,QAMjD,OAJHqB,GAAe,IACjBD,EAAIC,GAAYlB,GAGXiB,CAAA,GACN,IACFnB,OAAOqB,UAEN,MAAArC,OAAA,EAAAA,EAAOU,UAAWuB,EAAavB,QACjCL,EAAMuB,KAAK,CACTrD,MAAO,0CACP+D,OAAQ,YAIHpC,EAAAqC,EAAAA,IAAIN,GAAa,GACzB,CAAC5B,EAAOlD,EAAW+C,EAAUF,IAE1BwC,EAAsBC,EAAAA,SAAQ,MAC7B,MAAAzC,OAAA,EAAAA,EAAOU,iBAAWvD,WAAWuD,UAI3B,MAAAV,OAAA,EAAAA,EAAO0C,OAAOxB,GAAM/D,EAAU8D,MAAMO,IAAM,MAAAA,OAAA,EAAAA,EAAGlD,OAAO,MAAA4C,OAAA,EAAAA,EAAGH,YAC7D,CAACf,EAAO7C,IAGLgE,EAAiBsB,EAAAA,SACrB,IACEtF,GAAaA,EAAUuD,OAAS,EAC5BvD,EAAU6D,QAAQQ,GAAa,MAAPxB,OAAO,EAAAA,EAAAiB,MAAMC,GAAMA,EAAEH,OAASS,EAAElD,OACxD,IACN,CAACnB,EAAW6C,IAGR2C,EAAsBF,EAAAA,SAAQ,KAC7B,MAAAzC,OAAA,EAAAA,EAAOU,SAAWS,EAAeT,OAI/BV,EACJxC,KAAI,CAAC0D,EAAG0B,IAAYA,IAAWzB,EAAeI,WAAWC,GAAMA,EAAElD,KAAO4C,EAAEH,OAAQ,KAAOG,IACzFF,OAAOqB,SALD,IAMR,CAACrC,EAAOmB,IAEL0B,EAAoBJ,EAAAA,SACxB,MACc,MAAXtF,OAAW,EAAAA,EAAAuD,UAAsB,MAAXvD,OAAW,EAAAA,EAAAuD,SAAUvD,EAAUuF,OAAOrB,GAASA,EAAK/C,IAAM+C,EAAK9C,SACxF,CAACpB,IASH,OANA2F,EAAAA,WAAU,KACJH,EAAoBjC,OAAS,GAAK8B,GACjBR,GACrB,GACC,CAACW,EAAqBH,EAAqBR,IAEzCa,EAKFpE,EAAAA,IAAAmB,EAAA,CAAiBI,MAAO,CAAC7C,aACxB4B,SAACC,EAAAA,KAAAC,QAAA,CAAMC,MAAO,EACXH,SAAA,EAAA,MAAAgB,OAAA,EAAAA,EAASW,QAAS,EACjBjC,EAAAA,IAAAsE,WAAA,CAGGhE,SAAAgB,EAAQvC,KAAKwF,GACQ,SAAhBA,EAAOC,KAENxE,EAAAA,IAAAyE,EAAAA,mBAAA,CAECF,SACAG,WAAYxG,EAAMwG,WAClBC,YAAazG,EAAMyG,YACnBC,YAAa1G,EAAM0G,YACnBC,cAAe3G,EAAM2G,eALhBN,EAAOO,KAUX,SAGT,YAcHpG,WAAWuD,QAAS,GAAKS,EAAeT,OAASvD,EAAUuD,OACzD1B,EAAAA,KAAAC,EAAAA,MAAA,CAAMC,MAAO,EAGXH,SAAA,CAAU5B,EAAAuD,OAAS,EACjBjC,EAAAA,IAAA+E,EAAAA,KAAA,CAAKC,QAASC,KAAKC,IAAIxG,EAAUuD,OAAQ,GAAIkD,IAAK,EAChD7E,SAAU5B,EAAAK,KAAK8B,GACbb,EAAAA,IAAAoF,EAAAA,OAAA,CAEClF,KAAK,UACLmF,KAAK,QACLC,SAAU,EACVC,SAAU7D,GAAYkC,QAAe,MAAPrC,OAAO,EAAAA,EAAAiB,MAAMI,GAASA,EAAKN,OAASzB,EAAShB,MAC3E2F,KAAM3E,EAAShB,GAAGX,cAClBuG,KAAMC,EAAAA,QACNC,QAAS,IAAM7D,EAAkBjB,EAAShB,KAPrCgB,EAAShB,QAWlB,KACHG,EAAAA,IAAAoF,EAAAA,OAAA,CACClF,KAAK,UACLmF,KAAK,QACLE,SAAU7D,GAAaH,IAAS,MAAAA,OAAA,EAAAA,EAAOU,UAAqB,MAAXvD,OAAW,EAAAA,EAAAuD,QAC5DwD,KAAMC,EAAAA,QACNF,MAES,MAAPjE,OAAO,EAAAA,EAAAU,QAAA,eAAAG,OAED1D,EAAUuD,OAASV,EAAMU,QAAW,EAAI,WAAA,aAErB,IAArBvD,EAAUuD,qBACHvD,EAAU,GAAGoB,MACpB,UAAA,oBAEN6F,QAAS,IAAM7D,SAGjB,gBA/EA/B,EAAS,CAAA,EAmFrB,CC5OA,IAAe6F,EAACC,IACd,MAAMxH,WAACA,EAAAK,UAAYA,EAAWoH,KAAAA,GAAQD,EAChCE,EAA2B,iBAATD,EAAoBA,EAAOA,EAAKpG,KAClDsG,EAAYvG,EAAgBsG,GAC5BE,EAAaxG,EAAgBsG,GAAU,GAE7C,OAAOG,cAAY,CACjBxG,KAAMsG,EACNlG,MAAO,0BACPgG,KAAM,QAGNK,WAAY,CACVC,MAAO/E,GAETM,QAAS,CAACtD,aAAYK,aAGtB2H,GAAI,CACFH,cACMI,EAAAA,EAAA,CAAA,EAAgB,iBAATR,EAAoB,CAAA,EAAKA,GAAA,CAAA,EAAA,CACpCpG,KAAMuG,EACNH,KAAMG,MAGVM,WAAaC,GACXA,EAAKC,QAAgBhI,MAAO8C,EAAOmF,KAxCzC,IAAAC,EAAAC,EAAAC,EAyCQ,IAAKtF,EACI,OAAA,EAGT,MAAMpD,EAASuI,EAAQI,UAAU,CAACzI,eAC5B0I,EAA+BxI,MAAMC,QAAQ,OAAAoI,EAAA,OAASD,EAAA,MAAAD,OAAA,EAAAA,EAAAZ,WAAM,EAAAa,EAAAhF,cAAS,EAAAiF,EAAAlI,WACvEgI,EAASZ,KAAMnE,QAAQjD,UACvBH,MAAMC,QAAQV,KACdA,UACM,OAAA+I,EAAS,MAAAH,OAAA,EAAAA,EAAAZ,WAAM,EAAAe,EAAAlF,QAAQjD,UAAUP,IAE3C,GAAIoD,GAASA,EAAMU,OAAS8E,EAAiB9E,OAC3C,MACE8E,uBAAAA,OAA4B,IAA5BA,EAAiB9E,OAA6B8E,SAAAA,GAAAA,OAAAA,EAAiB9E,OAAA,WAInE,MAAM+E,GAAyB,MAAPzF,OAAO,EAAAA,EAAAU,QAC3BV,EAAMgB,QAAQK,IAAUmE,EAAiBvE,MAAM3B,GAAa+B,EAAKN,OAASzB,EAAShB,OACnF,GACJ,GAAImH,EAAgB/E,OACX,MAAA,CACLgF,QAAS,uEACTC,MAAOF,EAAgBjI,KAAK6D,GAAS,CAAC,CAACN,KAAMM,EAAKN,UAStD,MAAM6E,GAAmB,MAAA5F,OAAA,EAAAA,EAAOU,QAC5BV,EACGgB,QAAQK,GAASgB,QAAQ,MAAAhB,OAAA,EAAAA,EAAMN,QAC/BmB,QAAO,CAACC,EAAK0D,IACR1D,EAAI0D,EAAI9E,MACVgE,EAAAA,EAAA,CAAA,EAAW5C,GAAK,CAAA,EAAA,CAAA,CAAC0D,EAAI9E,MAAO,IAAIoB,EAAI0D,EAAI9E,MAAO8E,KAE1Cd,EAAAA,EAAA,CAAA,EACF5C,GAAA,CAAA,EAAA,CACH,CAAC0D,EAAI9E,MAAO,CAAC8E,MAEd,CAAA,GACL,GACEC,EAAkBC,OAAOC,OAAOJ,GACnC5E,QAAQK,IAAe,MAANA,OAAM,EAAAA,EAAAX,QAAS,IAChCuF,OACH,OAAIH,EAAgBpF,QACX,CACLgF,QAAS,2CACTC,MAAOG,EAAgBtI,KAAK6D,GAAS,CAAC,CAACN,KAAMM,EAAKN,SAI/C,KAEZ,EChGH,SAAwBmF,EAAuBvJ,GAE7C,MAA8B,cAA1BA,EAAMsD,WAAW9B,MAAwBxB,EAAMqD,MAC1CrD,EAAMwJ,cAAcpB,EAAAA,EAAA,CAAA,EACtBpI,GAAA,CAAA,EAAA,CACH4B,MAAO,GACP6H,MAAO,KAIJzJ,EAAMoC,QACf,CCVO,SAASsH,EAAsBC,GAChC,WAACA,WAAa5F,QACT,OAGT,MAAM6F,EAAmBD,EAAY9I,KAAK0D,GAAMA,EAAEkF,QAE9C,OAAAG,EAAiBC,SAAS,SACrB,WACED,EAAiBC,SAAS,WAC5B,eADE,CAKb,CCFA,SAAwBC,EAAuB9J,GAC7C,MAAM+J,EAAcC,EAAaA,aAAAhK,EAAMiK,KAAKhJ,MAAM,OAE5CiJ,EAAc9B,EAAAA,EAAA,CAAA,EACfpI,EAAMmK,YAAA,CAAA,EAAA,CAET/G,QAASpD,EAAMmK,WAAW/G,QAAQiB,QAAQ+F,GAAiB,UAAXA,EAAE9D,MAA+B,UAAX8D,EAAE5I,OAGxE6B,MAAOrD,EAAMqD,SAGTgF,WAACA,EAAAhF,MAAYA,EAAOE,SAAAA,EAAAC,SAAUA,GAAY0G,GAG1C1J,UAACA,GAAauC,EAAA,QAAMsH,WAAWvH,GAE/BwH,EAAoBxE,EAAAA,SAAQ,KAhCpC,IAAA2C,EAgC0C,OAAA,OAAAA,EAAA,MAAAsB,OAAA,EAAAA,EAAalJ,KAAK0D,GAAMA,EAAEH,UAAS,EAAC,GAAG,CAAC2F,IAC1EQ,KAAwB,MAAX/J,OAAW,EAAAA,EAAAuD,SAASvD,EAAU8D,MAAMO,GAAMA,EAAElD,KAAO0B,EAAMe,OAGtEoG,EAAkB3G,EAAAA,aACrBC,IACMT,IAAU,MAAA7C,OAAA,EAAAA,EAAWuD,SAAWvD,EAAU8D,MAAMO,GAAMA,EAAElD,KAAOmC,KAIpEP,EAAS,CAACqC,EAAAA,IAAI9B,EAAY,CAAC,UAAS,GAEtC,CAACP,EAAUF,EAAO7C,IAIdiK,EAAc5G,EAAAA,aAAY,KAC9BN,EAASmH,UAAO,GACf,CAACnH,IAEJ,OAAK/C,EAKFsB,EAAAA,IAAAC,EAAAA,KAAA,CAAK4I,WAAY,EAAG3I,KAAM0H,EAAsBrB,GAC/CjG,SAACC,EAAAA,KAAAC,QAAA,CAAMC,MAAO,EACZH,SAAA,CAACN,EAAAA,IAAAC,EAAAA,KAAA,CAAKC,KAAK,UACRI,WACEN,EAAAA,IAAA8I,QAAA,CAAMC,OAAK,EAACnI,KAAM,EAChBN,SAAMiB,EAAAe,OAGRtC,EAAAA,IAAAgJ,aAAA,CACCC,OAASjJ,EAAAA,IAAAoF,SAAA,CAAOE,SAAU,EAAGE,KAAA,WAAApD,OAAiBb,EAAMe,KAAA,OACpDzC,GAAO0B,GAAAA,OAAAA,EAAMe,KAAA,eACb4G,KACGlJ,EAAAA,IAAAmJ,OAAA,CACE7I,SAAU5B,EAAAK,KAAK8B,GACbb,EAAAA,IAAAoJ,EAAAA,SAAA,CACC7D,SAAUiD,EAAkBT,SAASlH,EAAShB,IAC9CyF,SAAU,EAEVE,KAAM3E,EAAShB,GAAGwJ,oBAClB1D,QAAS,IAAM+C,EAAgB7H,EAAShB,KAFnCgB,EAAShB,QAOtByJ,UAAU,QACVC,QAAS,CAACC,QAAQ,OAIvBjJ,EAAAA,KAAAkJ,EAAAA,KAAA,CAAKC,MAAM,SAASvE,IAAK,EACxB7E,SAAA,CAACN,EAAAA,IAAAC,EAAAA,KAAA,CAAK0J,KAAM,EAAGzJ,KAAK,UACjBI,SAAMpC,EAAAmK,WAAWzD,YAAY1G,EAAMmK,cAGrCrI,EAAAA,IAAAC,EAAAA,KAAA,CAAKC,KAAK,UACTI,SAACN,EAAAA,IAAAoF,SAAA,CACCC,KAAK,QACLI,KAAMmE,EAAAA,iBACN1J,KAAK,WACLqF,SAAU7D,EACViE,QAASgD,oBA5CXkB,UAAQ,CAAA,EAmDpB,CC9FA,IAAeC,EAACjE,IACR,MAAAC,KAACA,GAAQD,EAETI,EAAaxG,EADc,iBAATqG,EAAoBA,EAAOA,EAAKpG,MACX,GAE7C,OAAOwG,cAAY,CACjBxG,KAAMuG,EACNnG,wCAAkCgG,GAClCA,KAAM,SAGNK,WAAY,CAGVvD,KAAMoF,GAIR+B,OAAQ,kBACCjE,EAEHI,cAAY,CACVxG,KAAM,QACNoG,OAGAK,WAAY,CAGV6D,MAAOvC,YAKN3B,GAAA,CAAA,EAAA,CACHpG,KAAM,QACNyG,WAAY,CACV6D,MAAOvC,MAIjBwC,QAAS,CACPC,OAAQ,CACNpK,MAAO,QACPqK,SAAU,UAGf,EClDH,MAAMC,EAA+B,CACnC1L,UAAW,GACX2L,WAAY,IAGDC,EAAyBC,EAAAA,cAA2B,WAA6B,IAA5B1E,yDAASuE,EACnE,MAAA/L,WAACA,EAAa,aAAAK,UAAcA,EAAW2L,WAAAA,GAAkBD,EAAAA,EAAAA,CAAAA,EAAAA,GAAmBvE,GAE3E,MAAA,CACLnG,KAAM,wCAEN8K,OAAQjM,MAAMC,QAAQE,QAClB,EACA,CACEyH,WAAY,CACVsE,OAASvM,GACPqC,EAAAA,KAAA+D,WAAA,CACEhE,SAAA,CAACN,EAAAA,IAAAhC,EAAA,CAAQK,aAAwBK,cAChCR,EAAMwJ,cAAcxJ,QAKjCwM,OAAQ,CACNC,MAAO,IACFN,EAAWtL,KAAK+G,GAASF,EAAM,CAACE,OAAMzH,aAAYK,mBAClD2L,EAAWtL,KAAK+G,GAASgE,EAAO,CAAChE,aAI5C,kBXvBqB,IAAM/H,EAAQ6M,MAAM,CAPlB,KAO4B/M"}
@@ -1,16 +1,107 @@
1
+ import type {ArraySchemaType} from 'sanity'
1
2
  import {Plugin as Plugin_2} from 'sanity'
2
- import {RuleTypeConstraint} from 'sanity'
3
+ import type {Rule} from 'sanity'
4
+ import type {RuleTypeConstraint} from 'sanity'
5
+ import type {SanityClient} from 'sanity'
6
+
7
+ export declare type AllowedType = 'string' | 'number' | 'boolean' | 'text' | 'reference'
8
+
9
+ export declare type ArrayConfig = {
10
+ name: string
11
+ type: AllowedType
12
+ languages: Language[]
13
+ title?: string
14
+ group?: string
15
+ hidden?: boolean | (() => boolean)
16
+ readOnly?: boolean | (() => boolean)
17
+ validation?: Rule | Rule[]
18
+ field?: {
19
+ [key: string]: any
20
+ options: {
21
+ [key: string]: any
22
+ }
23
+ }
24
+ }
25
+
26
+ export declare type ArraySchemaWithLanguageOptions = ArraySchemaType & {
27
+ options: {
28
+ languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
29
+ apiVersion: string
30
+ }
31
+ }
32
+
33
+ export declare const clear: () => void
3
34
 
4
35
  export declare const internationalizedArray: Plugin_2<PluginConfig>
5
36
 
6
- declare type Language = {
7
- id: string
37
+ export declare type Language = {
38
+ id: Intl.UnicodeBCP47LocaleIdentifier
8
39
  title: string
9
40
  }
10
41
 
11
- declare type PluginConfig = {
12
- languages: Language[] | (() => Promise<Language[]>)
42
+ export declare type PluginConfig = {
43
+ /**
44
+ * https://www.sanity.io/docs/api-versioning
45
+ * @defaultValue '2022-11-27'
46
+ */
47
+ apiVersion?: string
48
+ /**
49
+ * You can give it an array of language definitions:
50
+ * ```tsx
51
+ * {
52
+ * languages: [
53
+ * {id: 'en', title: 'English'},
54
+ * {id: 'fr', title: 'French'}
55
+ * ]
56
+ * }
57
+ * ```
58
+ * You can load them async by passing a function that returns a promise:
59
+ * ```tsx
60
+ * {
61
+ * languages: async () => {
62
+ * const response = await fetch('https://example.com/languages')
63
+ * return response.json()
64
+ * }
65
+ * }
66
+ * ```
67
+ * You can query your dataset for languages::
68
+ * ```tsx
69
+ * {
70
+ * languages: (client) =>
71
+ * query.fetch(groq`*[_type == "language"]{id,title}`)
72
+ * }
73
+ * ```
74
+ */
75
+ languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
76
+ /**
77
+ * Can be a string matching core field types, as well as custom ones:
78
+ * ```tsx
79
+ * {
80
+ * fieldTypes: [
81
+ * "date", "datetime", "file", "image", "number", "string", "text", "url"
82
+ * ]
83
+ * }
84
+ * ```
85
+ * You can also define a type directly:
86
+ * ```tsx
87
+ * {
88
+ * fieldTypes: [
89
+ * defineField({
90
+ * name: 'featuredProduct',
91
+ * type: 'reference',
92
+ * to: [{type: 'product'}]
93
+ * hidden: (({document}) => !document?.title)
94
+ * })
95
+ * ]
96
+ * }
97
+ * ```
98
+ */
13
99
  fieldTypes: (string | RuleTypeConstraint)[]
14
100
  }
15
101
 
102
+ export declare type Value = {
103
+ _key: string
104
+ value?: string
105
+ }
106
+
16
107
  export {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-internationalized-array",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Store localized fields in an array to save on attributes",
5
5
  "keywords": [
6
6
  "sanity",
@@ -47,39 +47,40 @@
47
47
  "watch": "pkg-utils watch"
48
48
  },
49
49
  "dependencies": {
50
- "@sanity/icons": "^1.3.1",
50
+ "@sanity/icons": "^2.0.0",
51
51
  "@sanity/incompatible-plugin": "^1.0.4",
52
- "styled-components": "^5.3.5"
52
+ "@sanity/ui": "^1.0.0",
53
+ "suspend-react": "^0.0.8"
53
54
  },
54
55
  "devDependencies": {
55
- "@commitlint/cli": "^17.2.0",
56
- "@commitlint/config-conventional": "^17.2.0",
56
+ "@commitlint/cli": "^17.3.0",
57
+ "@commitlint/config-conventional": "^17.3.0",
57
58
  "@sanity/pkg-utils": "^1.18.0",
58
- "@sanity/plugin-kit": "^2.1.16",
59
+ "@sanity/plugin-kit": "^2.1.17",
59
60
  "@sanity/semantic-release-preset": "^2.0.2",
60
- "@sanity/ui": "1.0.0-beta.32",
61
61
  "@types/styled-components": "^5.1.26",
62
- "@typescript-eslint/eslint-plugin": "^5.43.0",
63
- "@typescript-eslint/parser": "^5.43.0",
64
- "eslint": "^8.27.0",
62
+ "@typescript-eslint/eslint-plugin": "^5.44.0",
63
+ "@typescript-eslint/parser": "^5.44.0",
64
+ "eslint": "^8.28.0",
65
65
  "eslint-config-prettier": "^8.5.0",
66
66
  "eslint-config-sanity": "^6.0.0",
67
67
  "eslint-plugin-prettier": "^4.2.1",
68
- "eslint-plugin-react": "^7.31.10",
68
+ "eslint-plugin-react": "^7.31.11",
69
69
  "eslint-plugin-react-hooks": "^4.6.0",
70
70
  "husky": "^8.0.2",
71
- "lint-staged": "^13.0.3",
72
- "prettier": "^2.7.1",
71
+ "lint-staged": "^13.0.4",
72
+ "prettier": "^2.8.0",
73
73
  "prettier-plugin-packagejson": "^2.3.0",
74
74
  "react": "^18",
75
75
  "rimraf": "^3.0.2",
76
- "sanity": "3.0.0-rc.2",
76
+ "sanity": "3.0.0-rc.3",
77
+ "styled-components": "^5.3.6",
77
78
  "typescript": "^4.9.3"
78
79
  },
79
80
  "peerDependencies": {
80
- "@sanity/ui": "1.0.0-beta.32",
81
81
  "react": "^18",
82
- "sanity": "dev-preview || 3.0.0-rc.2"
82
+ "sanity": "dev-preview || 3.0.0-rc.3 || ^3.0.0",
83
+ "styled-components": "^5.2"
83
84
  },
84
85
  "engines": {
85
86
  "node": ">=14"
package/src/cache.ts ADDED
@@ -0,0 +1,18 @@
1
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
+
3
+ import * as suspend from 'suspend-react'
4
+ import type {Language} from './types'
5
+
6
+ export const namespace = 'sanity-plugin-internationalized-array'
7
+
8
+ export const version = 'v0'
9
+
10
+ // https://github.com/pmndrs/suspend-react#preloading
11
+ export const preload = (fn: () => Promise<Language[]>) =>
12
+ suspend.preload(() => fn(), [version, namespace])
13
+
14
+ // https://github.com/pmndrs/suspend-react#cache-busting
15
+ export const clear = () => suspend.clear([version, namespace])
16
+
17
+ // https://github.com/pmndrs/suspend-react#peeking-into-entries-outside-of-suspense
18
+ export const peek = () => suspend.peek([version, namespace]) as Language[] | undefined
@@ -1,4 +1,4 @@
1
- import React, {useCallback, useEffect, useMemo} from 'react'
1
+ import React, {Fragment, useCallback, useEffect, useMemo} from 'react'
2
2
  import {
3
3
  insert,
4
4
  set,
@@ -6,14 +6,17 @@ import {
6
6
  ArrayOfObjectsItemMember,
7
7
  ArrayOfObjectsItem,
8
8
  ArrayOfObjectsInputProps,
9
+ useClient,
9
10
  } from 'sanity'
10
- import {Button, Grid, Spinner, Stack, useToast} from '@sanity/ui'
11
+ import {Button, Grid, Stack, useToast} from '@sanity/ui'
11
12
  import {AddIcon} from '@sanity/icons'
13
+ import {suspend} from 'suspend-react'
12
14
 
13
- import {Language, Value, ArraySchemaWithLanguageOptions} from '../types'
15
+ import type {Value, ArraySchemaWithLanguageOptions} from '../types'
14
16
  import Feedback from './Feedback'
15
17
  // TODO: Move this provider to the root component
16
18
  import {LanguageProvider} from './languageContext'
19
+ import {namespace, version} from '../cache'
17
20
 
18
21
  export type InternationalizedArrayProps = ArrayOfObjectsInputProps<
19
22
  Value,
@@ -26,21 +29,17 @@ export default function InternationalizedArray(props: InternationalizedArrayProp
26
29
  const {options} = schemaType
27
30
  const toast = useToast()
28
31
 
29
- const [languages, setLanguages] = React.useState<Language[] | null>(
30
- Array.isArray(options.languages) ? options.languages : null
31
- )
32
- // Resolve async languages
33
- useEffect(() => {
34
- async function resolveLanguages() {
35
- const resolvedLanguages = Array.isArray(options.languages)
36
- ? options.languages
37
- : await options.languages()
38
- setLanguages(resolvedLanguages)
39
- }
40
- if (!languages && !Array.isArray(options?.languages)) {
41
- resolveLanguages()
42
- }
43
- }, [languages, options])
32
+ const {apiVersion} = options
33
+ const client = useClient({apiVersion})
34
+ const languages = Array.isArray(options.languages)
35
+ ? options.languages
36
+ : // eslint-disable-next-line require-await
37
+ suspend(async () => {
38
+ if (typeof options.languages === 'function') {
39
+ return options.languages(client)
40
+ }
41
+ return options.languages
42
+ }, [version, namespace])
44
43
 
45
44
  const handleAddLanguage = useCallback(
46
45
  (languageId?: string) => {
@@ -169,10 +168,6 @@ export default function InternationalizedArray(props: InternationalizedArrayProp
169
168
  return <Feedback />
170
169
  }
171
170
 
172
- if (!languages) {
173
- return <Spinner />
174
- }
175
-
176
171
  return (
177
172
  <LanguageProvider value={{languages}}>
178
173
  <Stack space={2}>
@@ -2,7 +2,7 @@ import {ObjectItemProps, useFormValue} from 'sanity'
2
2
  import React, {useCallback, useMemo} from 'react'
3
3
  import {unset, set} from 'sanity'
4
4
  import {Button, Flex, Label, MenuButton, Menu, MenuItem, Card, Spinner, Stack} from '@sanity/ui'
5
- import {RemoveIcon} from '@sanity/icons'
5
+ import {RemoveCircleIcon} from '@sanity/icons'
6
6
 
7
7
  import {getToneFromValidation} from './getToneFromValidation'
8
8
  import {LanguageContext} from './languageContext'
@@ -91,8 +91,8 @@ export default function InternationalizedInput(props: ObjectItemProps<Internatio
91
91
 
92
92
  <Card tone="inherit">
93
93
  <Button
94
- mode="ghost"
95
- icon={RemoveIcon}
94
+ mode="bleed"
95
+ icon={RemoveCircleIcon}
96
96
  tone="critical"
97
97
  disabled={readOnly}
98
98
  onClick={handleUnset}
@@ -0,0 +1,18 @@
1
+ import {peek, preload} from '../cache'
2
+ import {memo} from 'react'
3
+ import type {PluginConfig} from '../types'
4
+ import {useClient} from 'sanity'
5
+
6
+ export default memo(function Preload(
7
+ props: Required<Pick<PluginConfig, 'apiVersion' | 'languages'>>
8
+ ) {
9
+ const client = useClient({apiVersion: props.apiVersion})
10
+ if (!Array.isArray(peek())) {
11
+ // eslint-disable-next-line require-await
12
+ preload(async () =>
13
+ Array.isArray(props.languages) ? props.languages : props.languages(client)
14
+ )
15
+ }
16
+
17
+ return null
18
+ })
package/src/index.ts CHANGED
@@ -1 +1,3 @@
1
+ export {clear} from './cache'
1
2
  export {internationalizedArray} from './plugin'
3
+ export * from './types'
package/src/plugin.tsx CHANGED
@@ -1,21 +1,36 @@
1
+ import React from 'react'
1
2
  import {definePlugin} from 'sanity'
2
- import {PluginConfig} from './types'
3
+ import Preload from './components/Preload'
3
4
  import array from './schema/array'
4
5
  import object from './schema/object'
6
+ import {PluginConfig} from './types'
5
7
 
6
- const CONFIG_DEFAULT = {
8
+ const CONFIG_DEFAULT: PluginConfig = {
7
9
  languages: [],
8
10
  fieldTypes: [],
9
11
  }
10
12
 
11
13
  export const internationalizedArray = definePlugin<PluginConfig>((config = CONFIG_DEFAULT) => {
12
- const {languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}
14
+ const {apiVersion = '2022-11-27', languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}
13
15
 
14
16
  return {
15
17
  name: 'sanity-plugin-internationalized-array',
18
+ // If `languages` is a callback then let's preload it
19
+ studio: Array.isArray(languages)
20
+ ? undefined
21
+ : {
22
+ components: {
23
+ layout: (props) => (
24
+ <>
25
+ <Preload apiVersion={apiVersion} languages={languages} />
26
+ {props.renderDefault(props)}
27
+ </>
28
+ ),
29
+ },
30
+ },
16
31
  schema: {
17
32
  types: [
18
- ...fieldTypes.map((type) => array({type, languages})),
33
+ ...fieldTypes.map((type) => array({type, apiVersion, languages})),
19
34
  ...fieldTypes.map((type) => object({type})),
20
35
  ],
21
36
  },
@@ -1,16 +1,19 @@
1
- import {defineField, FieldDefinition, Rule} from 'sanity'
1
+ /* eslint-disable no-nested-ternary */
2
+ import {defineField, type FieldDefinition, type Rule, type SanityClient} from 'sanity'
3
+ import {peek} from '../cache'
2
4
 
3
5
  import {createFieldName} from '../components/createFieldName'
4
6
  import InternationalizedArray from '../components/InternationalizedArray'
5
7
  import {Language, Value} from '../types'
6
8
 
7
9
  type ArrayFactoryConfig = {
8
- languages: Language[] | (() => Promise<Language[]>)
10
+ apiVersion: string
11
+ languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
9
12
  type: string | FieldDefinition
10
13
  }
11
14
 
12
15
  export default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {
13
- const {languages, type} = config
16
+ const {apiVersion, languages, type} = config
14
17
  const typeName = typeof type === `string` ? type : type.name
15
18
  const arrayName = createFieldName(typeName)
16
19
  const objectName = createFieldName(typeName, true)
@@ -24,11 +27,12 @@ export default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {
24
27
  components: {
25
28
  input: InternationalizedArray,
26
29
  },
27
- options: {languages},
30
+ options: {apiVersion, languages},
28
31
  // TODO: Resolve this typing issue with the inner object
29
32
  // @ts-ignore
30
33
  of: [
31
34
  defineField({
35
+ ...(typeof type === 'string' ? {} : type),
32
36
  name: objectName,
33
37
  type: objectName,
34
38
  }),
@@ -39,9 +43,12 @@ export default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {
39
43
  return true
40
44
  }
41
45
 
46
+ const client = context.getClient({apiVersion})
42
47
  const contextLanguages: Language[] = Array.isArray(context?.type?.options?.languages)
43
- ? context?.type?.options.languages
44
- : await context?.type?.options.languages()
48
+ ? context!.type!.options.languages
49
+ : Array.isArray(peek())
50
+ ? peek()
51
+ : await context?.type?.options.languages(client)
45
52
 
46
53
  if (value && value.length > contextLanguages.length) {
47
54
  return `Cannot be more than ${
package/src/types.ts CHANGED
@@ -1,7 +1,7 @@
1
- import {Rule, ArraySchemaType, RuleTypeConstraint} from 'sanity'
1
+ import type {Rule, ArraySchemaType, RuleTypeConstraint, SanityClient} from 'sanity'
2
2
 
3
3
  export type Language = {
4
- id: string
4
+ id: Intl.UnicodeBCP47LocaleIdentifier
5
5
  title: string
6
6
  }
7
7
 
@@ -25,13 +25,68 @@ export type Value = {
25
25
  }
26
26
 
27
27
  export type PluginConfig = {
28
- // array of languages or async function that returns array of languages
29
- languages: Language[] | (() => Promise<Language[]>)
28
+ /**
29
+ * https://www.sanity.io/docs/api-versioning
30
+ * @defaultValue '2022-11-27'
31
+ */
32
+ apiVersion?: string
33
+ /**
34
+ * You can give it an array of language definitions:
35
+ * ```tsx
36
+ * {
37
+ * languages: [
38
+ * {id: 'en', title: 'English'},
39
+ * {id: 'fr', title: 'French'}
40
+ * ]
41
+ * }
42
+ * ```
43
+ * You can load them async by passing a function that returns a promise:
44
+ * ```tsx
45
+ * {
46
+ * languages: async () => {
47
+ * const response = await fetch('https://example.com/languages')
48
+ * return response.json()
49
+ * }
50
+ * }
51
+ * ```
52
+ * You can query your dataset for languages::
53
+ * ```tsx
54
+ * {
55
+ * languages: (client) =>
56
+ * query.fetch(groq`*[_type == "language"]{id,title}`)
57
+ * }
58
+ * ```
59
+ */
60
+ languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
61
+ /**
62
+ * Can be a string matching core field types, as well as custom ones:
63
+ * ```tsx
64
+ * {
65
+ * fieldTypes: [
66
+ * "date", "datetime", "file", "image", "number", "string", "text", "url"
67
+ * ]
68
+ * }
69
+ * ```
70
+ * You can also define a type directly:
71
+ * ```tsx
72
+ * {
73
+ * fieldTypes: [
74
+ * defineField({
75
+ * name: 'featuredProduct',
76
+ * type: 'reference',
77
+ * to: [{type: 'product'}]
78
+ * hidden: (({document}) => !document?.title)
79
+ * })
80
+ * ]
81
+ * }
82
+ * ```
83
+ */
30
84
  fieldTypes: (string | RuleTypeConstraint)[]
31
85
  }
32
86
 
33
87
  export type ArraySchemaWithLanguageOptions = ArraySchemaType & {
34
88
  options: {
35
- languages: Language[] | (() => Promise<Language[]>)
89
+ languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
90
+ apiVersion: string
36
91
  }
37
92
  }