sanity-plugin-internationalized-array 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,7 +20,7 @@ or
20
20
  yarn add sanity-plugin-internationalized-array
21
21
  ```
22
22
 
23
- ## Usage (simple)
23
+ ## Usage for simple field types
24
24
 
25
25
  Add it as a plugin in sanity.config.ts (or .js):
26
26
 
@@ -56,7 +56,27 @@ defineField({
56
56
  }),
57
57
  ```
58
58
 
59
- ## Usage (advanced)
59
+ ## Loading languages
60
+
61
+ Languages must be an array of objects with an `id` and `title`.
62
+
63
+ ```ts
64
+ languages: [
65
+ {id: 'en', title: 'English'},
66
+ {id: 'fr', title: 'French'}
67
+ ],
68
+ ```
69
+
70
+ Or an asynchronous function that returns an array of objects with an `id` and `title`.
71
+
72
+ ```ts
73
+ languages: async () => {
74
+ const response = await fetch('https://example.com/languages')
75
+ return response.json()
76
+ }
77
+ ```
78
+
79
+ ## Using more complex field types
60
80
 
61
81
  For more control over the `value` field, you can pass a schema definition into the `fieldTypes` array.
62
82
 
package/lib/index.esm.js CHANGED
@@ -1,2 +1,2 @@
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 l=null!=arguments[i]?arguments[i]:{};i%2?e(Object(l),!0).forEach((function(e){t(n,e,l[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(n,Object.getOwnPropertyDescriptors(l)):e(Object(l)).forEach((function(e){Object.defineProperty(n,e,Object.getOwnPropertyDescriptor(l,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 i,setIfMissing as l,set as r,ArrayOfObjectsItem as o,defineField as a,useFormBuilder as d,unset as u,useFormValue as c,definePlugin as s}from"sanity";import{jsx as p,jsxs as g,Fragment as h}from"react/jsx-runtime";import{useMemo as y,useCallback as m}from"react";import{Card as f,Stack as v,Text as k,Code as b,useToast as _,Button as O,Grid as j,Flex as P,Box as w,Label as x,MenuButton as C,Menu as z,MenuItem as I}from"@sanity/ui";import{RestoreIcon as T,AddIcon as A,SyncIcon as S,RemoveIcon as B}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 E(e){let n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return n?["internationalizedArray",D(e),"Value"].join(""):["internationalizedArray",D(e)].join("")}const U={languages:[{id:"en",title:"English"},{id:"no",title:"Norsk"}]};function F(){return p(f,{tone:"caution",border:!0,radius:2,padding:3,children:g(v,{space:4,children:[g(k,{children:["An array of language objects must be passed into the ",p("code",{children:"internationalizedArray"})," ","helper function, each with an ",p("code",{children:"id"})," and ",p("code",{children:"title"})," field. Example:"]}),p(f,{padding:2,border:!0,radius:2,children:p(b,{size:1,language:"javascript",children:JSON.stringify(U,null,2)})})]})})}function N(e){const{members:t,value:a,schemaType:d,onChange:u}=e,c="boolean"==typeof d.readOnly&&d.readOnly,{options:s}=d,f=_(),k=y((()=>{var e;return null!=(e=null==s?void 0:s.languages)?e:[]}),[s]),b=m((e=>{const t={_type:"".concat(d.name,"Value")},r=e?[n(n({},t),{},{_key:e})]:k.filter((e=>!(null==a?void 0:a.length)||!a.find((n=>n._key===e.id)))).map((e=>n(n({},t),{},{_key:e.id}))),o=(null==a?void 0:a.length)?a.map((e=>e)):[],c=r.map((e=>{const n=k.findIndex((n=>e._key===n.id)),t=k.slice(n+1),l=o.findIndex((e=>t.find((n=>n.id===e._key))));return l<0?o.push(e):o.splice(l,0,e),i([e],l<0?"after":"before",[l])}));u([l([]),...c])}),[k,u,a]),P=m((()=>{if(!(null==a?void 0:a.length))return;const e=a.reduce(((e,n)=>{const t=k.findIndex((e=>e.id===(null==n?void 0:n._key)));return t>-1&&(e[t]=n),e}),[]).filter(Boolean);(null==a?void 0:a.length)!==e.length&&f.push({title:"There was an error reordering languages",status:"warning"}),u(r(e))}),[f,k,u,a]),w=y((()=>null==a?void 0:a.every((e=>k.find((n=>(null==n?void 0:n.id)===(null==e?void 0:e._key)))))),[a,k]),x=k.filter((e=>null==a?void 0:a.find((n=>n._key===e.id)))),C=y((()=>(null==a?void 0:a.length)?a.map(((e,n)=>n===x.findIndex((n=>n.id===e._key))?null:e)).filter(Boolean):[]),[a,x]);return y((()=>!(null==k?void 0:k.length)||(null==k?void 0:k.length)&&k.every((e=>e.id&&e.title))),[k])?g(v,{space:2,children:[(null==t?void 0:t.length)>0?p(h,{children:t.map((n=>"item"===n.kind?p(o,{member:n,renderItem:e.renderItem,renderField:e.renderField,renderInput:e.renderInput,renderPreview:e.renderPreview},n.key):null))}):null,C.length>0&&w?p(O,{tone:"caution",icon:T,onClick:()=>P(),text:"Restore order of languages"}):null,(null==k?void 0:k.length)>0&&x.length<k.length?g(v,{space:2,children:[p(j,{columns:Math.min(k.length,5),gap:2,children:k.map((e=>p(O,{tone:"primary",mode:"ghost",fontSize:1,disabled:c||Boolean(null==a?void 0:a.find((n=>n._key===e.id))),text:e.id.toUpperCase(),icon:A,onClick:()=>b(e.id)},e.id)))}),p(O,{tone:"primary",mode:"ghost",disabled:c||a&&(null==a?void 0:a.length)>=(null==k?void 0:k.length),icon:A,text:(null==a?void 0:a.length)?"Add missing ".concat(k.length-a.length==1?"language":"languages"):"Add all languages",onClick:()=>b()})]}):null]}):p(F,{})}var V=e=>{const{languages:t,type:i}=e,l="string"==typeof i?i:i.name,r=E(l),o=E(l,!0);return a({name:r,title:"Internationalized array",type:"array",components:{input:N},options:{languages:t},of:[a({name:o,type:o})],validation:e=>e.max(null==t?void 0:t.length).custom(((e,t)=>{var i,l;const{languages:r}=null!=(l=null==(i=null==t?void 0:t.type)?void 0:i.options)?l:{},o=(null==e?void 0:e.length)?e.filter((e=>!r.find((n=>e._key===n.id)))):[];if(o.length)return{message:"Array item keys must be valid languages registered to the field type",paths:o.map((e=>[{_key:e._key}]))};const a=(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]})),{}):{},d=Object.values(a).filter((e=>(null==e?void 0:e.length)>1)).flat();return!d.length||{message:"There can only be one field per language",paths:d.map((e=>[{_key:e._key}]))}}))})};function J(e){const{renderPreview:n}=d(),t=m((()=>{e.inputProps.onChange(u())}),[e.inputProps]);return"reference"===e.schemaType.name&&e.value?g(P,{gap:2,children:[p(w,{flex:1,children:n({layout:"default",schemaType:e.schemaType,value:e.value})}),p(O,{mode:"ghost",icon:S,onClick:t})]}):e.children}function L(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 M(e){var t,i;const l=c(e.path.slice(0,-1)),o=n(n({},e.inputProps),{},{members:e.inputProps.members.filter((e=>"field"===e.kind&&"value"===e.name)),value:e.value}),{validation:a,value:d,onChange:s,readOnly:h}=o,v=y((()=>{var n,t,i;return null!=(i=null==(t=null==(n=null==e?void 0:e.parentSchemaType)?void 0:n.options)?void 0:t.languages)?i:[]}),[null==(i=null==(t=null==e?void 0:e.parentSchemaType)?void 0:t.options)?void 0:i.languages]),k=y((()=>{var e;return null!=(e=null==l?void 0:l.map((e=>e._key)))?e:[]}),[l]),b=v.find((e=>e.id===d._key)),_=m((e=>{d&&v.find((n=>n.id===e))&&s([r(e,["_key"])])}),[s,d,v]),j=m((()=>{s(u())}),[s]);return p(f,{tone:L(a),children:g(P,{align:"center",gap:2,children:[p(f,{tone:"inherit",children:p(w,{children:b?p(x,{muted:!0,size:1,children:d._key}):p(C,{button:p(O,{fontSize:1,text:'Change "'.concat(d._key,'"')}),id:"".concat(d._key,"-change-key"),menu:p(z,{children:v.map((e=>p(I,{disabled:k.includes(e.id),fontSize:1,text:e.id.toLocaleUpperCase(),onClick:()=>_(e.id)},e.id)))}),placement:"right",popover:{portal:!0}})})}),p(f,{flex:1,tone:"inherit",children:e.inputProps.renderInput(e.inputProps)}),p(f,{tone:"inherit",children:p(O,{mode:"ghost",icon:B,tone:"critical",disabled:h,onClick:j})})]})})}var R=e=>{const{type:t}=e,i=E("string"==typeof t?t:t.name,!0);return a({name:i,title:"Internationalized array ".concat(t),type:"object",components:{item:M},fields:["string"==typeof t?a({name:"value",type:t,components:{field:J}}):n(n({},t),{},{name:"value",components:{field:J}})],preview:{select:{title:"value",subtitle:"_key"}}})};const q={languages:[],fieldTypes:[]},G=s((function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:q;const{languages:t,fieldTypes:i}=n(n({},q),e);return{name:"sanity-plugin-internationalized-array",schema:{types:[...i.map((e=>V({type:e,languages:t}))),...i.map((e=>R({type:e})))]}}}));export{G as internationalizedArray};
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};
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/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, {useCallback, useMemo} from 'react'\nimport {\n insert,\n set,\n setIfMissing,\n ArrayOfObjectsItemMember,\n ArrayOfObjectsItem,\n ArrayOfObjectsInputProps,\n} from 'sanity'\nimport {Button, Grid, Stack, useToast} from '@sanity/ui'\nimport {AddIcon, RestoreIcon} from '@sanity/icons'\n\nimport {Language, Value, ArraySchemaWithLanguageOptions} from '../types'\nimport Feedback from './Feedback'\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: Language[] = useMemo(() => options?.languages ?? [], [options])\n\n const handleAddLanguage = useCallback(\n (languageId?: string) => {\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, 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) {\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 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 = languages.filter((l) => value?.find((v) => v._key === l.id))\n const languagesOutOfOrder = useMemo(() => {\n if (!value?.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 if (!languagesAreValid) {\n return <Feedback />\n }\n\n return (\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 {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 once languages have values */}\n {languages?.length > 0 && languagesInUse.length < languages.length ? (\n <Stack space={2}>\n {/* No more than 5 columns */}\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 <Button\n tone=\"primary\"\n mode=\"ghost\"\n disabled={readOnly || (value && value?.length >= languages?.length)}\n icon={AddIcon}\n text={\n value?.length\n ? `Add missing ${languages.length - value.length === 1 ? `language` : `languages`}`\n : `Add all languages`\n }\n onClick={() => handleAddLanguage()}\n />\n </Stack>\n ) : null}\n </Stack>\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[]\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.max(languages?.length).custom<Value[]>((value, context) => {\n const {languages: contextLanguages}: {languages: Language[]} = context?.type?.options ?? {}\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 {SyncIcon} from '@sanity/icons'\nimport {Box, Button, Flex} from '@sanity/ui'\nimport React, {useCallback} from 'react'\nimport {FieldProps, unset, useFormBuilder} from 'sanity'\n\nexport default function InternationalizedField(props: FieldProps) {\n const {renderPreview} = useFormBuilder()\n const handleUnset = useCallback(() => {\n props.inputProps.onChange(unset())\n }, [props.inputProps])\n\n // Show reference field selector if there's a value\n if (props.schemaType.name === 'reference' && props.value) {\n // Shows just the preview, but not the button to remove the value\n // TODO: This is a stop-gap, restore default reference UI behaviour\n return (\n <Flex gap={2}>\n <Box flex={1}>\n {renderPreview({\n layout: 'default',\n schemaType: props.schemaType,\n value: props.value,\n })}\n </Box>\n <Button mode=\"ghost\" icon={SyncIcon} onClick={handleUnset} />\n </Flex>\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 {Box, Button, Flex, Label, MenuButton, Menu, MenuItem, Card} from '@sanity/ui'\nimport {RemoveIcon} from '@sanity/icons'\n\nimport {Language} from '../types'\nimport {getToneFromValidation} from './getToneFromValidation'\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 // TODO: fix TS support for overloading options\n const languages: Language[] = useMemo(\n // @ts-ignore\n () => props?.parentSchemaType?.options?.languages ?? [],\n // @ts-ignore\n [props?.parentSchemaType?.options?.languages]\n )\n const languageKeysInUse = useMemo(() => parentValue?.map((v) => v._key) ?? [], [parentValue])\n const keyIsValid = languages.find((l) => l.id === value._key)\n\n // Changes the key of this item, ideally to a valid language\n const handleKeyChange = useCallback(\n (languageId: string) => {\n if (!value || !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 return (\n <Card tone={getToneFromValidation(validation)}>\n <Flex align=\"center\" gap={2}>\n <Card tone=\"inherit\">\n <Box>\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 </Box>\n </Card>\n\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 </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","InternationalizedArray","props","members","value","schemaType","onChange","readOnly","options","toast","useToast","useMemo","_a","handleAddLanguage","useCallback","languageId","itemBase","_type","concat","newItems","_key","filter","length","find","v","languagesInUse","insertions","item","languageIndex","findIndex","l","remainingLanguages","nextLanguageIndex","r","push","splice","insert","setIfMissing","handleRestoreOrder","updatedValue","reduce","acc","newIndex","Boolean","status","set","allKeysAreLanguages","every","languagesOutOfOrder","vIndex","Fragment","member","kind","ArrayOfObjectsItem","renderItem","renderField","renderInput","renderPreview","key","Button","icon","RestoreIcon","onClick","text","Grid","columns","Math","min","gap","mode","fontSize","disabled","AddIcon","array","config","type","typeName","arrayName","objectName","defineField","components","input","of","validation","rule","max","custom","context","_b","contextLanguages","nonLanguageKeys","message","paths","valuesByLanguage","cur","_objectSpread","duplicateValues","Object","values","flat","InternationalizedField","useFormBuilder","handleUnset","inputProps","unset","Flex","Box","flex","layout","SyncIcon","getToneFromValidation","validations","validationLevels","level","includes","InternationalizedInput","parentValue","useFormValue","path","inlineProps","m","_c","parentSchemaType","languageKeysInUse","keyIsValid","handleKeyChange","align","Label","muted","MenuButton","button","menu","Menu","MenuItem","toLocaleUpperCase","placement","popover","portal","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/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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"}
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 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 l=null!=arguments[i]?arguments[i]:{};i%2?e(Object(l),!0).forEach((function(e){t(n,e,l[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(n,Object.getOwnPropertyDescriptors(l)):e(Object(l)).forEach((function(e){Object.defineProperty(n,e,Object.getOwnPropertyDescriptor(l,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("sanity"),l=require("react/jsx-runtime"),r=require("react"),a=require("@sanity/ui"),o=require("@sanity/icons");function s(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 d(e){let n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return n?["internationalizedArray",s(e),"Value"].join(""):["internationalizedArray",s(e)].join("")}const u={languages:[{id:"en",title:"English"},{id:"no",title:"Norsk"}]};function c(){return l.jsx(a.Card,{tone:"caution",border:!0,radius:2,padding:3,children:l.jsxs(a.Stack,{space:4,children:[l.jsxs(a.Text,{children:["An array of language objects must be passed into the ",l.jsx("code",{children:"internationalizedArray"})," ","helper function, each with an ",l.jsx("code",{children:"id"})," and ",l.jsx("code",{children:"title"})," field. Example:"]}),l.jsx(a.Card,{padding:2,border:!0,radius:2,children:l.jsx(a.Code,{size:1,language:"javascript",children:JSON.stringify(u,null,2)})})]})})}function p(e){const{members:t,value:s,schemaType:d,onChange:u}=e,p="boolean"==typeof d.readOnly&&d.readOnly,{options:g}=d,y=a.useToast(),h=r.useMemo((()=>{var e;return null!=(e=null==g?void 0:g.languages)?e:[]}),[g]),m=r.useCallback((e=>{const t={_type:"".concat(d.name,"Value")},l=e?[n(n({},t),{},{_key:e})]:h.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=l.map((e=>{const n=h.findIndex((n=>e._key===n.id)),t=h.slice(n+1),l=r.findIndex((e=>t.find((n=>n.id===e._key))));return l<0?r.push(e):r.splice(l,0,e),l<0?i.insert([e],"after",[l]):i.insert([e],"before",[l])}));u([i.setIfMissing([]),...a])}),[h,u,s]),f=r.useCallback((()=>{if(!(null==s?void 0:s.length))return;const e=s.reduce(((e,n)=>{const t=h.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&&y.push({title:"There was an error reordering languages",status:"warning"}),u(i.set(e))}),[y,h,u,s]),v=r.useMemo((()=>null==s?void 0:s.every((e=>h.find((n=>(null==n?void 0:n.id)===(null==e?void 0:e._key)))))),[s,h]),j=h.filter((e=>null==s?void 0:s.find((n=>n._key===e.id)))),x=r.useMemo((()=>(null==s?void 0:s.length)?s.map(((e,n)=>n===j.findIndex((n=>n.id===e._key))?null:e)).filter(Boolean):[]),[s,j]);return r.useMemo((()=>!(null==h?void 0:h.length)||(null==h?void 0:h.length)&&h.every((e=>e.id&&e.title))),[h])?l.jsxs(a.Stack,{space:2,children:[(null==t?void 0:t.length)>0?l.jsx(l.Fragment,{children:t.map((n=>"item"===n.kind?l.jsx(i.ArrayOfObjectsItem,{member:n,renderItem:e.renderItem,renderField:e.renderField,renderInput:e.renderInput,renderPreview:e.renderPreview},n.key):null))}):null,x.length>0&&v?l.jsx(a.Button,{tone:"caution",icon:o.RestoreIcon,onClick:()=>f(),text:"Restore order of languages"}):null,(null==h?void 0:h.length)>0&&j.length<h.length?l.jsxs(a.Stack,{space:2,children:[l.jsx(a.Grid,{columns:Math.min(h.length,5),gap:2,children:h.map((e=>l.jsx(a.Button,{tone:"primary",mode:"ghost",fontSize:1,disabled:p||Boolean(null==s?void 0:s.find((n=>n._key===e.id))),text:e.id.toUpperCase(),icon:o.AddIcon,onClick:()=>m(e.id)},e.id)))}),l.jsx(a.Button,{tone:"primary",mode:"ghost",disabled:p||s&&(null==s?void 0:s.length)>=(null==h?void 0:h.length),icon:o.AddIcon,text:(null==s?void 0:s.length)?"Add missing ".concat(h.length-s.length==1?"language":"languages"):"Add all languages",onClick:()=>m()})]}):null]}):l.jsx(c,{})}var g=e=>{const{languages:t,type:l}=e,r="string"==typeof l?l:l.name,a=d(r),o=d(r,!0);return i.defineField({name:a,title:"Internationalized array",type:"array",components:{input:p},options:{languages:t},of:[i.defineField({name:o,type:o})],validation:e=>e.max(null==t?void 0:t.length).custom(((e,t)=>{var i,l;const{languages:r}=null!=(l=null==(i=null==t?void 0:t.type)?void 0:i.options)?l:{},a=(null==e?void 0:e.length)?e.filter((e=>!r.find((n=>e._key===n.id)))):[];if(a.length)return{message:"Array item keys must be valid languages registered to the field type",paths:a.map((e=>[{_key:e._key}]))};const o=(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]})),{}):{},s=Object.values(o).filter((e=>(null==e?void 0:e.length)>1)).flat();return!s.length||{message:"There can only be one field per language",paths:s.map((e=>[{_key:e._key}]))}}))})};function y(e){const{renderPreview:n}=i.useFormBuilder(),t=r.useCallback((()=>{e.inputProps.onChange(i.unset())}),[e.inputProps]);return"reference"===e.schemaType.name&&e.value?l.jsxs(a.Flex,{gap:2,children:[l.jsx(a.Box,{flex:1,children:n({layout:"default",schemaType:e.schemaType,value:e.value})}),l.jsx(a.Button,{mode:"ghost",icon:o.SyncIcon,onClick:t})]}):e.children}function h(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 m(e){var t,s;const d=i.useFormValue(e.path.slice(0,-1)),u=n(n({},e.inputProps),{},{members:e.inputProps.members.filter((e=>"field"===e.kind&&"value"===e.name)),value:e.value}),{validation:c,value:p,onChange:g,readOnly:y}=u,m=r.useMemo((()=>{var n,t,i;return null!=(i=null==(t=null==(n=null==e?void 0:e.parentSchemaType)?void 0:n.options)?void 0:t.languages)?i:[]}),[null==(s=null==(t=null==e?void 0:e.parentSchemaType)?void 0:t.options)?void 0:s.languages]),f=r.useMemo((()=>{var e;return null!=(e=null==d?void 0:d.map((e=>e._key)))?e:[]}),[d]),v=m.find((e=>e.id===p._key)),j=r.useCallback((e=>{p&&m.find((n=>n.id===e))&&g([i.set(e,["_key"])])}),[g,p,m]),x=r.useCallback((()=>{g(i.unset())}),[g]);return l.jsx(a.Card,{tone:h(c),children:l.jsxs(a.Flex,{align:"center",gap:2,children:[l.jsx(a.Card,{tone:"inherit",children:l.jsx(a.Box,{children:v?l.jsx(a.Label,{muted:!0,size:1,children:p._key}):l.jsx(a.MenuButton,{button:l.jsx(a.Button,{fontSize:1,text:'Change "'.concat(p._key,'"')}),id:"".concat(p._key,"-change-key"),menu:l.jsx(a.Menu,{children:m.map((e=>l.jsx(a.MenuItem,{disabled:f.includes(e.id),fontSize:1,text:e.id.toLocaleUpperCase(),onClick:()=>j(e.id)},e.id)))}),placement:"right",popover:{portal:!0}})})}),l.jsx(a.Card,{flex:1,tone:"inherit",children:e.inputProps.renderInput(e.inputProps)}),l.jsx(a.Card,{tone:"inherit",children:l.jsx(a.Button,{mode:"ghost",icon:o.RemoveIcon,tone:"critical",disabled:y,onClick:x})})]})})}var f=e=>{const{type:t}=e,l=d("string"==typeof t?t:t.name,!0);return i.defineField({name:l,title:"Internationalized array ".concat(t),type:"object",components:{item:m},fields:["string"==typeof t?i.defineField({name:"value",type:t,components:{field:y}}):n(n({},t),{},{name:"value",components:{field:y}})],preview:{select:{title:"value",subtitle:"_key"}}})};const v={languages:[],fieldTypes:[]},j=i.definePlugin((function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:v;const{languages:t,fieldTypes:i}=n(n({},v),e);return{name:"sanity-plugin-internationalized-array",schema:{types:[...i.map((e=>g({type:e,languages:t}))),...i.map((e=>f({type:e})))]}}}));exports.internationalizedArray=j;
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=_;
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/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, {useCallback, useMemo} from 'react'\nimport {\n insert,\n set,\n setIfMissing,\n ArrayOfObjectsItemMember,\n ArrayOfObjectsItem,\n ArrayOfObjectsInputProps,\n} from 'sanity'\nimport {Button, Grid, Stack, useToast} from '@sanity/ui'\nimport {AddIcon, RestoreIcon} from '@sanity/icons'\n\nimport {Language, Value, ArraySchemaWithLanguageOptions} from '../types'\nimport Feedback from './Feedback'\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: Language[] = useMemo(() => options?.languages ?? [], [options])\n\n const handleAddLanguage = useCallback(\n (languageId?: string) => {\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, 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) {\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 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 = languages.filter((l) => value?.find((v) => v._key === l.id))\n const languagesOutOfOrder = useMemo(() => {\n if (!value?.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 if (!languagesAreValid) {\n return <Feedback />\n }\n\n return (\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 {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 once languages have values */}\n {languages?.length > 0 && languagesInUse.length < languages.length ? (\n <Stack space={2}>\n {/* No more than 5 columns */}\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 <Button\n tone=\"primary\"\n mode=\"ghost\"\n disabled={readOnly || (value && value?.length >= languages?.length)}\n icon={AddIcon}\n text={\n value?.length\n ? `Add missing ${languages.length - value.length === 1 ? `language` : `languages`}`\n : `Add all languages`\n }\n onClick={() => handleAddLanguage()}\n />\n </Stack>\n ) : null}\n </Stack>\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[]\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.max(languages?.length).custom<Value[]>((value, context) => {\n const {languages: contextLanguages}: {languages: Language[]} = context?.type?.options ?? {}\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 {SyncIcon} from '@sanity/icons'\nimport {Box, Button, Flex} from '@sanity/ui'\nimport React, {useCallback} from 'react'\nimport {FieldProps, unset, useFormBuilder} from 'sanity'\n\nexport default function InternationalizedField(props: FieldProps) {\n const {renderPreview} = useFormBuilder()\n const handleUnset = useCallback(() => {\n props.inputProps.onChange(unset())\n }, [props.inputProps])\n\n // Show reference field selector if there's a value\n if (props.schemaType.name === 'reference' && props.value) {\n // Shows just the preview, but not the button to remove the value\n // TODO: This is a stop-gap, restore default reference UI behaviour\n return (\n <Flex gap={2}>\n <Box flex={1}>\n {renderPreview({\n layout: 'default',\n schemaType: props.schemaType,\n value: props.value,\n })}\n </Box>\n <Button mode=\"ghost\" icon={SyncIcon} onClick={handleUnset} />\n </Flex>\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 {Box, Button, Flex, Label, MenuButton, Menu, MenuItem, Card} from '@sanity/ui'\nimport {RemoveIcon} from '@sanity/icons'\n\nimport {Language} from '../types'\nimport {getToneFromValidation} from './getToneFromValidation'\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 // TODO: fix TS support for overloading options\n const languages: Language[] = useMemo(\n // @ts-ignore\n () => props?.parentSchemaType?.options?.languages ?? [],\n // @ts-ignore\n [props?.parentSchemaType?.options?.languages]\n )\n const languageKeysInUse = useMemo(() => parentValue?.map((v) => v._key) ?? [], [parentValue])\n const keyIsValid = languages.find((l) => l.id === value._key)\n\n // Changes the key of this item, ideally to a valid language\n const handleKeyChange = useCallback(\n (languageId: string) => {\n if (!value || !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 return (\n <Card tone={getToneFromValidation(validation)}>\n <Flex align=\"center\" gap={2}>\n <Card tone=\"inherit\">\n <Box>\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 </Box>\n </Card>\n\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 </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","InternationalizedArray","props","members","value","schemaType","onChange","readOnly","options","toast","useToast","useMemo","_a","handleAddLanguage","useCallback","languageId","itemBase","_type","concat","newItems","_key","filter","length","find","v","languagesInUse","insertions","item","languageIndex","findIndex","l","remainingLanguages","nextLanguageIndex","r","push","splice","insert","setIfMissing","handleRestoreOrder","updatedValue","reduce","acc","newIndex","Boolean","status","set","allKeysAreLanguages","every","languagesOutOfOrder","vIndex","Fragment","member","kind","ArrayOfObjectsItem","renderItem","renderField","renderInput","renderPreview","key","Button","icon","RestoreIcon","onClick","text","Grid","columns","Math","min","gap","mode","fontSize","disabled","AddIcon","array","config","type","typeName","arrayName","objectName","defineField","components","input","of","validation","rule","max","custom","context","_b","contextLanguages","nonLanguageKeys","message","paths","valuesByLanguage","cur","_objectSpread","duplicateValues","Object","values","flat","InternationalizedField","useFormBuilder","handleUnset","inputProps","unset","Flex","Box","flex","layout","SyncIcon","getToneFromValidation","validations","validationLevels","level","includes","InternationalizedInput","parentValue","useFormValue","path","inlineProps","m","_c","parentSchemaType","languageKeysInUse","keyIsValid","handleKeyChange","align","Label","muted","MenuButton","button","menu","Menu","MenuItem","toLocaleUpperCase","placement","popover","portal","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/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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"}
@@ -9,7 +9,7 @@ declare type Language = {
9
9
  }
10
10
 
11
11
  declare type PluginConfig = {
12
- languages: Language[]
12
+ languages: Language[] | (() => Promise<Language[]>)
13
13
  fieldTypes: (string | RuleTypeConstraint)[]
14
14
  }
15
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-internationalized-array",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "Store localized fields in an array to save on attributes",
5
5
  "keywords": [
6
6
  "sanity",
@@ -1,4 +1,4 @@
1
- import React, {useCallback, useMemo} from 'react'
1
+ import React, {useCallback, useEffect, useMemo} from 'react'
2
2
  import {
3
3
  insert,
4
4
  set,
@@ -7,11 +7,13 @@ import {
7
7
  ArrayOfObjectsItem,
8
8
  ArrayOfObjectsInputProps,
9
9
  } from 'sanity'
10
- import {Button, Grid, Stack, useToast} from '@sanity/ui'
11
- import {AddIcon, RestoreIcon} from '@sanity/icons'
10
+ import {Button, Grid, Spinner, Stack, useToast} from '@sanity/ui'
11
+ import {AddIcon} from '@sanity/icons'
12
12
 
13
13
  import {Language, Value, ArraySchemaWithLanguageOptions} from '../types'
14
14
  import Feedback from './Feedback'
15
+ // TODO: Move this provider to the root component
16
+ import {LanguageProvider} from './languageContext'
15
17
 
16
18
  export type InternationalizedArrayProps = ArrayOfObjectsInputProps<
17
19
  Value,
@@ -24,10 +26,28 @@ export default function InternationalizedArray(props: InternationalizedArrayProp
24
26
  const {options} = schemaType
25
27
  const toast = useToast()
26
28
 
27
- const languages: Language[] = useMemo(() => options?.languages ?? [], [options])
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])
28
44
 
29
45
  const handleAddLanguage = useCallback(
30
46
  (languageId?: string) => {
47
+ if (!languages?.length) {
48
+ return
49
+ }
50
+
31
51
  const itemBase = {_type: `${schemaType.name}Value`}
32
52
 
33
53
  // Create new items
@@ -73,12 +93,12 @@ export default function InternationalizedArray(props: InternationalizedArrayProp
73
93
 
74
94
  onChange([setIfMissing([]), ...insertions])
75
95
  },
76
- [languages, onChange, value]
96
+ [languages, onChange, schemaType.name, value]
77
97
  )
78
98
 
79
99
  // TODO: This is lazy, reordering and re-setting the whole array – it could be surgical
80
100
  const handleRestoreOrder = useCallback(() => {
81
- if (!value?.length) {
101
+ if (!value?.length || !languages?.length) {
82
102
  return
83
103
  }
84
104
 
@@ -107,13 +127,24 @@ export default function InternationalizedArray(props: InternationalizedArrayProp
107
127
  }, [toast, languages, onChange, value])
108
128
 
109
129
  const allKeysAreLanguages = useMemo(() => {
130
+ if (!value?.length || !languages?.length) {
131
+ return true
132
+ }
133
+
110
134
  return value?.every((v) => languages.find((l) => l?.id === v?._key))
111
135
  }, [value, languages])
112
136
 
113
137
  // Check languages are in the correct order
114
- const languagesInUse = languages.filter((l) => value?.find((v) => v._key === l.id))
138
+ const languagesInUse = useMemo(
139
+ () =>
140
+ languages && languages.length > 1
141
+ ? languages.filter((l) => value?.find((v) => v._key === l.id))
142
+ : [],
143
+ [languages, value]
144
+ )
145
+
115
146
  const languagesOutOfOrder = useMemo(() => {
116
- if (!value?.length) {
147
+ if (!value?.length || !languagesInUse.length) {
117
148
  return []
118
149
  }
119
150
 
@@ -128,76 +159,98 @@ export default function InternationalizedArray(props: InternationalizedArrayProp
128
159
  [languages]
129
160
  )
130
161
 
162
+ useEffect(() => {
163
+ if (languagesOutOfOrder.length > 0 && allKeysAreLanguages) {
164
+ handleRestoreOrder()
165
+ }
166
+ }, [languagesOutOfOrder, allKeysAreLanguages, handleRestoreOrder])
167
+
131
168
  if (!languagesAreValid) {
132
169
  return <Feedback />
133
170
  }
134
171
 
172
+ if (!languages) {
173
+ return <Spinner />
174
+ }
175
+
135
176
  return (
136
- <Stack space={2}>
137
- {members?.length > 0 ? (
138
- <>
139
- {/* TODO: Resolve type for ArrayOfObjectsItemMember */}
140
- {/* @ts-ignore */}
141
- {members.map((member: ArrayOfObjectsItemMember) => {
142
- if (member.kind === 'item') {
143
- return (
144
- <ArrayOfObjectsItem
145
- key={member.key}
146
- member={member}
147
- renderItem={props.renderItem}
148
- renderField={props.renderField}
149
- renderInput={props.renderInput}
150
- renderPreview={props.renderPreview}
151
- />
152
- )
153
- }
154
-
155
- return null
156
- })}
157
- </>
158
- ) : null}
159
- {languagesOutOfOrder.length > 0 && allKeysAreLanguages ? (
160
- <Button
161
- tone="caution"
162
- icon={RestoreIcon}
163
- onClick={() => handleRestoreOrder()}
164
- text="Restore order of languages"
165
- />
166
- ) : null}
167
-
168
- {/* Show buttons if languages are configured */}
169
- {/* Hide them once languages have values */}
170
- {languages?.length > 0 && languagesInUse.length < languages.length ? (
171
- <Stack space={2}>
172
- {/* No more than 5 columns */}
173
- <Grid columns={Math.min(languages.length, 5)} gap={2}>
174
- {languages.map((language) => (
175
- <Button
176
- key={language.id}
177
- tone="primary"
178
- mode="ghost"
179
- fontSize={1}
180
- disabled={readOnly || Boolean(value?.find((item) => item._key === language.id))}
181
- text={language.id.toUpperCase()}
182
- icon={AddIcon}
183
- onClick={() => handleAddLanguage(language.id)}
184
- />
185
- ))}
186
- </Grid>
177
+ <LanguageProvider value={{languages}}>
178
+ <Stack space={2}>
179
+ {members?.length > 0 ? (
180
+ <>
181
+ {/* TODO: Resolve type for ArrayOfObjectsItemMember */}
182
+ {/* @ts-ignore */}
183
+ {members.map((member: ArrayOfObjectsItemMember) => {
184
+ if (member.kind === 'item') {
185
+ return (
186
+ <ArrayOfObjectsItem
187
+ key={member.key}
188
+ member={member}
189
+ renderItem={props.renderItem}
190
+ renderField={props.renderField}
191
+ renderInput={props.renderInput}
192
+ renderPreview={props.renderPreview}
193
+ />
194
+ )
195
+ }
196
+
197
+ return null
198
+ })}
199
+ </>
200
+ ) : null}
201
+
202
+ {/* This now happens automatically */}
203
+ {/* {languagesOutOfOrder.length > 0 && allKeysAreLanguages ? (
187
204
  <Button
188
- tone="primary"
189
- mode="ghost"
190
- disabled={readOnly || (value && value?.length >= languages?.length)}
191
- icon={AddIcon}
192
- text={
193
- value?.length
194
- ? `Add missing ${languages.length - value.length === 1 ? `language` : `languages`}`
195
- : `Add all languages`
196
- }
197
- onClick={() => handleAddLanguage()}
205
+ tone="caution"
206
+ icon={RestoreIcon}
207
+ onClick={() => handleRestoreOrder()}
208
+ text="Restore order of languages"
198
209
  />
199
- </Stack>
200
- ) : null}
201
- </Stack>
210
+ ) : null} */}
211
+
212
+ {/* Show buttons if languages are configured */}
213
+ {/* Hide them if all languages have values */}
214
+ {languages?.length > 0 && languagesInUse.length < languages.length ? (
215
+ <Stack space={2}>
216
+ {/* Hide language-specific buttons if there's only one */}
217
+ {/* No more than 5 columns */}
218
+ {languages.length > 1 ? (
219
+ <Grid columns={Math.min(languages.length, 5)} gap={2}>
220
+ {languages.map((language) => (
221
+ <Button
222
+ key={language.id}
223
+ tone="primary"
224
+ mode="ghost"
225
+ fontSize={1}
226
+ disabled={readOnly || Boolean(value?.find((item) => item._key === language.id))}
227
+ text={language.id.toUpperCase()}
228
+ icon={AddIcon}
229
+ onClick={() => handleAddLanguage(language.id)}
230
+ />
231
+ ))}
232
+ </Grid>
233
+ ) : null}
234
+ <Button
235
+ tone="primary"
236
+ mode="ghost"
237
+ disabled={readOnly || (value && value?.length >= languages?.length)}
238
+ icon={AddIcon}
239
+ text={
240
+ // eslint-disable-next-line no-nested-ternary
241
+ value?.length
242
+ ? `Add missing ${
243
+ languages.length - value.length === 1 ? `language` : `languages`
244
+ }`
245
+ : languages.length === 1
246
+ ? `Add ${languages[0].title} Field`
247
+ : `Add all languages`
248
+ }
249
+ onClick={() => handleAddLanguage()}
250
+ />
251
+ </Stack>
252
+ ) : null}
253
+ </Stack>
254
+ </LanguageProvider>
202
255
  )
203
256
  }
@@ -1,30 +1,13 @@
1
- import {SyncIcon} from '@sanity/icons'
2
- import {Box, Button, Flex} from '@sanity/ui'
3
- import React, {useCallback} from 'react'
4
- import {FieldProps, unset, useFormBuilder} from 'sanity'
1
+ import {FieldProps} from 'sanity'
5
2
 
6
3
  export default function InternationalizedField(props: FieldProps) {
7
- const {renderPreview} = useFormBuilder()
8
- const handleUnset = useCallback(() => {
9
- props.inputProps.onChange(unset())
10
- }, [props.inputProps])
11
-
12
4
  // Show reference field selector if there's a value
13
5
  if (props.schemaType.name === 'reference' && props.value) {
14
- // Shows just the preview, but not the button to remove the value
15
- // TODO: This is a stop-gap, restore default reference UI behaviour
16
- return (
17
- <Flex gap={2}>
18
- <Box flex={1}>
19
- {renderPreview({
20
- layout: 'default',
21
- schemaType: props.schemaType,
22
- value: props.value,
23
- })}
24
- </Box>
25
- <Button mode="ghost" icon={SyncIcon} onClick={handleUnset} />
26
- </Flex>
27
- )
6
+ return props.renderDefault({
7
+ ...props,
8
+ title: '',
9
+ level: 0,
10
+ })
28
11
  }
29
12
 
30
13
  return props.children
@@ -1,11 +1,11 @@
1
1
  import {ObjectItemProps, useFormValue} from 'sanity'
2
2
  import React, {useCallback, useMemo} from 'react'
3
3
  import {unset, set} from 'sanity'
4
- import {Box, Button, Flex, Label, MenuButton, Menu, MenuItem, Card} from '@sanity/ui'
4
+ import {Button, Flex, Label, MenuButton, Menu, MenuItem, Card, Spinner, Stack} from '@sanity/ui'
5
5
  import {RemoveIcon} from '@sanity/icons'
6
6
 
7
- import {Language} from '../types'
8
7
  import {getToneFromValidation} from './getToneFromValidation'
8
+ import {LanguageContext} from './languageContext'
9
9
 
10
10
  type InternationalizedValue = {
11
11
  _type: string
@@ -28,20 +28,15 @@ export default function InternationalizedInput(props: ObjectItemProps<Internatio
28
28
  const {validation, value, onChange, readOnly} = inlineProps
29
29
 
30
30
  // The parent array contains the languages from the plugin config
31
- // TODO: fix TS support for overloading options
32
- const languages: Language[] = useMemo(
33
- // @ts-ignore
34
- () => props?.parentSchemaType?.options?.languages ?? [],
35
- // @ts-ignore
36
- [props?.parentSchemaType?.options?.languages]
37
- )
31
+ const {languages} = React.useContext(LanguageContext)
32
+
38
33
  const languageKeysInUse = useMemo(() => parentValue?.map((v) => v._key) ?? [], [parentValue])
39
- const keyIsValid = languages.find((l) => l.id === value._key)
34
+ const keyIsValid = languages?.length ? languages.find((l) => l.id === value._key) : false
40
35
 
41
36
  // Changes the key of this item, ideally to a valid language
42
37
  const handleKeyChange = useCallback(
43
38
  (languageId: string) => {
44
- if (!value || !languages.find((l) => l.id === languageId)) {
39
+ if (!value || !languages?.length || !languages.find((l) => l.id === languageId)) {
45
40
  return
46
41
  }
47
42
 
@@ -55,53 +50,56 @@ export default function InternationalizedInput(props: ObjectItemProps<Internatio
55
50
  onChange(unset())
56
51
  }, [onChange])
57
52
 
53
+ if (!languages) {
54
+ return <Spinner />
55
+ }
56
+
58
57
  return (
59
- <Card tone={getToneFromValidation(validation)}>
60
- <Flex align="center" gap={2}>
58
+ <Card paddingTop={2} tone={getToneFromValidation(validation)}>
59
+ <Stack space={2}>
61
60
  <Card tone="inherit">
62
- <Box>
63
- {keyIsValid ? (
64
- <Label muted size={1}>
65
- {value._key}
66
- </Label>
67
- ) : (
68
- <MenuButton
69
- button={<Button fontSize={1} text={`Change "${value._key}"`} />}
70
- id={`${value._key}-change-key`}
71
- menu={
72
- <Menu>
73
- {languages.map((language) => (
74
- <MenuItem
75
- disabled={languageKeysInUse.includes(language.id)}
76
- fontSize={1}
77
- key={language.id}
78
- text={language.id.toLocaleUpperCase()}
79
- onClick={() => handleKeyChange(language.id)}
80
- />
81
- ))}
82
- </Menu>
83
- }
84
- placement="right"
85
- popover={{portal: true}}
86
- />
87
- )}
88
- </Box>
61
+ {keyIsValid ? (
62
+ <Label muted size={1}>
63
+ {value._key}
64
+ </Label>
65
+ ) : (
66
+ <MenuButton
67
+ button={<Button fontSize={1} text={`Change "${value._key}"`} />}
68
+ id={`${value._key}-change-key`}
69
+ menu={
70
+ <Menu>
71
+ {languages.map((language) => (
72
+ <MenuItem
73
+ disabled={languageKeysInUse.includes(language.id)}
74
+ fontSize={1}
75
+ key={language.id}
76
+ text={language.id.toLocaleUpperCase()}
77
+ onClick={() => handleKeyChange(language.id)}
78
+ />
79
+ ))}
80
+ </Menu>
81
+ }
82
+ placement="right"
83
+ popover={{portal: true}}
84
+ />
85
+ )}
89
86
  </Card>
87
+ <Flex align="center" gap={2}>
88
+ <Card flex={1} tone="inherit">
89
+ {props.inputProps.renderInput(props.inputProps)}
90
+ </Card>
90
91
 
91
- <Card flex={1} tone="inherit">
92
- {props.inputProps.renderInput(props.inputProps)}
93
- </Card>
94
-
95
- <Card tone="inherit">
96
- <Button
97
- mode="ghost"
98
- icon={RemoveIcon}
99
- tone="critical"
100
- disabled={readOnly}
101
- onClick={handleUnset}
102
- />
103
- </Card>
104
- </Flex>
92
+ <Card tone="inherit">
93
+ <Button
94
+ mode="ghost"
95
+ icon={RemoveIcon}
96
+ tone="critical"
97
+ disabled={readOnly}
98
+ onClick={handleUnset}
99
+ />
100
+ </Card>
101
+ </Flex>
102
+ </Stack>
105
103
  </Card>
106
104
  )
107
105
  }
@@ -0,0 +1,9 @@
1
+ import React from 'react'
2
+
3
+ import {Language} from '../types'
4
+
5
+ export const LanguageContext = React.createContext<{languages: Language[]}>({
6
+ languages: [],
7
+ })
8
+
9
+ export const LanguageProvider = LanguageContext.Provider
@@ -5,7 +5,7 @@ import InternationalizedArray from '../components/InternationalizedArray'
5
5
  import {Language, Value} from '../types'
6
6
 
7
7
  type ArrayFactoryConfig = {
8
- languages: Language[]
8
+ languages: Language[] | (() => Promise<Language[]>)
9
9
  type: string | FieldDefinition
10
10
  }
11
11
 
@@ -34,8 +34,21 @@ export default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {
34
34
  }),
35
35
  ],
36
36
  validation: (rule: Rule) =>
37
- rule.max(languages?.length).custom<Value[]>((value, context) => {
38
- const {languages: contextLanguages}: {languages: Language[]} = context?.type?.options ?? {}
37
+ rule.custom<Value[]>(async (value, context) => {
38
+ if (!value) {
39
+ return true
40
+ }
41
+
42
+ const contextLanguages: Language[] = Array.isArray(context?.type?.options?.languages)
43
+ ? context?.type?.options.languages
44
+ : await context?.type?.options.languages()
45
+
46
+ if (value && value.length > contextLanguages.length) {
47
+ return `Cannot be more than ${
48
+ contextLanguages.length === 1 ? `1 item` : `${contextLanguages.length} items`
49
+ }`
50
+ }
51
+
39
52
  const nonLanguageKeys = value?.length
40
53
  ? value.filter((item) => !contextLanguages.find((language) => item._key === language.id))
41
54
  : []
package/src/types.ts CHANGED
@@ -25,12 +25,13 @@ export type Value = {
25
25
  }
26
26
 
27
27
  export type PluginConfig = {
28
- languages: Language[]
28
+ // array of languages or async function that returns array of languages
29
+ languages: Language[] | (() => Promise<Language[]>)
29
30
  fieldTypes: (string | RuleTypeConstraint)[]
30
31
  }
31
32
 
32
33
  export type ArraySchemaWithLanguageOptions = ArraySchemaType & {
33
34
  options: {
34
- languages: Language[]
35
+ languages: Language[] | (() => Promise<Language[]>)
35
36
  }
36
37
  }