@sanity/language-filter 4.0.5 → 4.1.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Sanity.io
3
+ Copyright (c) 2026 Sanity.io
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # @sanity/language-filter
2
2
 
3
- > This is a **Sanity Studio v3** plugin.
4
3
  > For the v2 version, please refer to the [v2 version](https://github.com/sanity-io/sanity/tree/next/packages/%40sanity/language-filter).
5
4
 
6
5
  # Field-level translation filter Plugin for Sanity.io
@@ -68,7 +67,7 @@ Add it as a plugin in sanity.config.ts (or .js), and configure it:
68
67
  defaultLanguages: ['nb'],
69
68
  // Only show language filter for document type `page` (schemaType.name)
70
69
  documentTypes: ['page'],
71
- filterField: (enclosingType, member, selectedLanguageIds) =>
70
+ filterField: (enclosingType, member, selectedLanguageIds, parentValue) =>
72
71
  !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(member.name),
73
72
  })
74
73
  ]
@@ -82,8 +81,12 @@ Config properties:
82
81
  -- A function that returns a promise resolving to an array of language objects with `id` and `title`. This is useful if you want to fetch the list of supported languages from an external source. See [Loading languages](#loading-languages) for more details.
83
82
  - `defaultLanguages` (optional) is an array of strings where each entry must match an `id` from the `supportedLanguages` array. These languages will be listed by default and will not be possible to unselect. If no `defaultLanguages` is configured, all localized fields will be selected by default.
84
83
  - `documentTypes` (optional) is an array of strings where each entry must match a `name` from your document schemas. If defined, this property will be used to conditionally show the language filter on specific document schema types. If undefined, the language filter will show on all document schema types.
85
- - `filterField` (optional) is a function that must return true if the field should be displayed. It is passed the enclosing type (e.g the object type containing the localized fields, the field, and an array of the currently selected language ids.
86
- This function is called for all fields and in objects for documents that have language filter enabled.
84
+ - `filterField` (optional) is a function that must return true if the field should be displayed. It receives:
85
+ - `enclosingType` (for example, the object type containing localized fields)
86
+ - `field` (the field/fieldset member being rendered)
87
+ - `selectedLanguageIds` (an array of currently selected language ids)
88
+ - `parentValue` (the current enclosing object value; `undefined` when not available)
89
+ This function is called for all fields and fieldsets in objects for documents that have language filter enabled.
87
90
  _Default:_ `!enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)`
88
91
  - `apiVersion` (optional) used for the Sanity Client when asynchronously loading languages.
89
92
 
package/lib/index.d.mts CHANGED
@@ -13,6 +13,7 @@ export declare type FilterFieldFunction = (
13
13
  enclosingType: ObjectSchemaType,
14
14
  field: FieldMember | FieldsetState,
15
15
  selectedLanguageIds: string[],
16
+ parentValue: Record<string, unknown> | undefined,
16
17
  ) => boolean
17
18
 
18
19
  export declare function isLanguageFilterEnabled(
package/lib/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export declare type FilterFieldFunction = (
13
13
  enclosingType: ObjectSchemaType,
14
14
  field: FieldMember | FieldsetState,
15
15
  selectedLanguageIds: string[],
16
+ parentValue: Record<string, unknown> | undefined,
16
17
  ) => boolean
17
18
 
18
19
  export declare function isLanguageFilterEnabled(
package/lib/index.esm.js CHANGED
@@ -1,3 +1,13 @@
1
- import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import{useState as r,createContext as o,useEffect as l,useMemo as a,useContext as i,useCallback as s}from"react";import{useClient as u,TextWithTone as c,useFormValue as d,useSchema as p,definePlugin as g,isObjectSchemaType as f}from"sanity";import{EyeClosedIcon as m,EyeOpenIcon as y,TranslateIcon as b,CheckmarkCircleIcon as h,CircleIcon as v}from"@sanity/icons";import{Box as L,useClickOutside as O,Stack as w,Card as j,Button as S,Flex as P,Text as T,TextInput as x,Popover as I,Badge as A}from"@sanity/ui";import{styled as D}from"styled-components";const k=(e,t,n)=>!e.name.startsWith("locale")||n.includes(t.name);function C(e,t){var n,r;const o=function(e){return"object"===(null==e?void 0:e.jsonType)&&"document"===F(e).name}(e)&&(null==(n=null==e?void 0:e.options)?void 0:n.languageFilter),l=!t.documentTypes;return!!(l&&!1!==o||!l&&o||e&&null!=(r=t.documentTypes)&&r.includes(e.name))}function F(e){return e.type?F(e.type):e}const N="@sanity/plugin/language-filter/selected-languages";function E(e){const t=z(e).map((e=>e.id));let n=t;try{const e=window.localStorage.getItem(N);e&&(n=JSON.parse(e))}catch(e){}return r=t,n=n.filter((e=>r.includes(e))),n;var r}function z({supportedLanguages:e,defaultLanguages:t}){return Array.isArray(e)?e.filter((e=>!(null!=t&&t.includes(e.id)))):[]}var J=Object.defineProperty,V=Object.defineProperties,$=Object.getOwnPropertyDescriptors,_=Object.getOwnPropertySymbols,H=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable,q=(e,t,n)=>t in e?J(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,B=(e,t)=>{for(var n in t||(t={}))H.call(t,n)&&q(e,n,t[n]);if(_)for(var n of _(t))W.call(t,n)&&q(e,n,t[n]);return e};const G={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:k},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},K=o(G);function M(t){const n=u({apiVersion:"2023-01-01"}),[o,i]=r(Array.isArray(t.options.supportedLanguages)?t.options.supportedLanguages:[]);l((()=>{let e=[];Array.isArray(t.options.supportedLanguages)||async function(t){e=await t(n,{}),i(e)}(t.options.supportedLanguages)}),[n,t.options.supportedLanguages]);const s=a((()=>{return e=B(B({},G.options),t.options),V(e,$({supportedLanguages:o}));var e}),[t.options,o]),[c,d]=function(e){return r((()=>{var t;return[...null!=(t=e.defaultLanguages)?t:[],...E(e)]}))}(s);return e(K.Provider,{value:{options:s,selectedLanguageIds:c,setSelectedLanguageIds:d},children:t.renderDefault(t)})}function Q(){return i(K)}const R=e=>Array.from(new Set(e));function U(){const{selectedLanguageIds:e,setSelectedLanguageIds:t,options:n}=Q(),{defaultLanguages:r=[]}=n,o=a((()=>z(n)),[n]),l=s((e=>{var n;t(R([...r,...e])),n=R([...r,...e]),window.localStorage.setItem(N,JSON.stringify(n))}),[r,t]),i=s((()=>l(o.map((e=>e.id)))),[l,o]),u=s((()=>{l(r)}),[r,l]),c=s((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):R([...n,t]),l(n)}),[l,e]);return{activeLanguages:a((()=>R([...null!=r?r:[],...e])),[r,e]),allSelected:e.length===o.length+r.length,selectAll:i,selectNone:u,toggleLanguage:c}}const X=D(L)`
1
+ import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import{useState as a,createContext as i,useEffect as l,useMemo as r,useContext as o,useCallback as s}from"react";import{useClient as d,TextWithTone as u,useFormValue as c,useSchema as g,definePlugin as p,isObjectSchemaType as f}from"sanity";import{EyeClosedIcon as m,EyeOpenIcon as h,TranslateIcon as L,CheckmarkCircleIcon as y,CircleIcon as S}from"@sanity/icons";import{Box as v,useClickOutside as T,Stack as b,Card as w,Button as A,Flex as x,Text as I,TextInput as k,Popover as C,Badge as F}from"@sanity/ui";import{styled as N}from"styled-components";const j=(e,t,n)=>!e.name.startsWith("locale")||n.includes(t.name);function D(e,t){const n=function(e){return"object"===e?.jsonType&&"document"===z(e).name}(e)&&e?.options?.languageFilter,a=!t.documentTypes;return!!(a&&!1!==n||!a&&n||e&&t.documentTypes?.includes(e.name))}function z(e){return e.type?z(e.type):e}const O="@sanity/plugin/language-filter/selected-languages";function J(e){const t=V(e).map((e=>e.id));let n=t;try{const e=window.localStorage.getItem(O);e&&(n=JSON.parse(e))}catch{}return a=t,n=n.filter((e=>a.includes(e))),n;var a}function V({supportedLanguages:e,defaultLanguages:t}){return Array.isArray(e)?e.filter((e=>!t?.includes(e.id))):[]}const $={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:j},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},_=i($);function E(){return o(_)}const H=e=>Array.from(new Set(e)),P=N(v)`
2
2
  max-height: calc(100vh - 200px);
3
- `;function Y(){const{options:o}=Q(),l=o.supportedLanguages.filter((e=>{var t;return null==(t=o.defaultLanguages)?void 0:t.includes(e.id)})),a=o.supportedLanguages.filter((e=>{var t;return!(null!=(t=o.defaultLanguages)&&t.includes(e.id))})),[i,u]=r(!1),{activeLanguages:d,allSelected:p,selectAll:g,selectNone:f,toggleLanguage:h}=U(),[v,A]=r(null),[D,k]=r(null),C=s((e=>{"ALL"===e.currentTarget.value?g():f()}),[g,f]),F=s((()=>u((e=>!e))),[]),N=s((()=>u(!1)),[]);O(N,[v,D]);const E=o.supportedLanguages.length,[z,J]=r(""),V=s((e=>{e.currentTarget.value?J(e.currentTarget.value):J("")}),[]),$=E>4,_=e(X,{overflow:"auto",children:t(w,{padding:1,space:1,children:[l.length>0&&t(n,{children:[l.map((t=>e(Z,{id:t.id,title:t.title,selected:!0},t.id))),e(j,{borderTop:!0})]}),e(S,{mode:"bleed",onClick:C,justify:"flex-start",value:p?"NONE":"ALL",disabled:!!z,children:t(P,{gap:3,align:"center",children:[e(T,{size:2,children:p?e(c,{tone:"primary",children:e(m,{})}):e(y,{})}),e(L,{flex:1,children:e(T,{children:p?"Hide all":"Show all"})})]})}),$?e(x,{onChange:V,value:z,placeholder:"Filter languages"}):e(j,{borderTop:!0}),a.filter((e=>!z||e.title.toLowerCase().includes(z.toLowerCase()))).map((t=>e(Z,{id:t.id,onToggle:h,selected:d.includes(t.id),title:t.title},t.id)))]})}),H=d.length===E?"Showing all":`Showing ${d.length} / ${E}`;return e(I,{animate:!0,content:_,open:i,portal:!0,ref:k,children:e(S,{text:H,icon:b,mode:"bleed",onClick:F,ref:A,selected:i})})}function Z(n){const{id:r,onToggle:o,selected:l,title:a}=n,i=s((()=>{o&&o(r)}),[r,o]),u=!o;return e(S,{mode:"bleed",onClick:i,justify:"flex-start",disabled:u,children:t(P,{gap:3,align:"center",children:[e(T,{size:2,children:l?e(c,{tone:u?"default":"positive",children:e(h,{})}):e(v,{})}),e(L,{flex:1,children:e(T,{children:a})}),e(A,{children:r})]})})}var ee=Object.defineProperty,te=Object.defineProperties,ne=Object.getOwnPropertyDescriptors,re=Object.getOwnPropertySymbols,oe=Object.prototype.hasOwnProperty,le=Object.prototype.propertyIsEnumerable,ae=(e,t,n)=>t in e?ee(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,ie=(e,t)=>{for(var n in t||(t={}))oe.call(t,n)&&ae(e,n,t[n]);if(re)for(var n of re(t))le.call(t,n)&&ae(e,n,t[n]);return e},se=(e,t)=>te(e,ne(t)),ue=(e,t)=>{var n={};for(var r in e)oe.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&re)for(var r of re(e))t.indexOf(r)<0&&le.call(e,r)&&(n[r]=e[r]);return n};function ce(e){const t=e,{members:n,schemaType:r,renderDefault:o}=t,l=ue(t,["members","schemaType","renderDefault"]),{selectedLanguageIds:i,options:s}=Q(),{filterField:u}=s,c=a((()=>n.filter((e=>"field"===e.kind&&u(r,e,i)||"fieldSet"===e.kind||"error"===e.kind)).map((e=>"fieldSet"===e.kind?se(ie({},e),{fieldSet:se(ie({},e.fieldSet),{members:e.fieldSet.members.filter((e=>"field"===e.kind&&u(r,e,i)))})}):e))),[r,n,u,i]);return o(se(ie({},l),{members:c,schemaType:r,renderDefault:o}))}var de=Object.defineProperty,pe=Object.defineProperties,ge=Object.getOwnPropertyDescriptors,fe=Object.getOwnPropertySymbols,me=Object.prototype.hasOwnProperty,ye=Object.prototype.propertyIsEnumerable,be=(e,t,n)=>t in e?de(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,he=(e,t)=>{for(var n in t||(t={}))me.call(t,n)&&be(e,n,t[n]);if(fe)for(var n of fe(t))ye.call(t,n)&&be(e,n,t[n]);return e};const ve=g((t=>{const n=()=>e(Y,{}),r=he(he({},G.options),t);return{name:"@sanity/language-filter",studio:{components:{layout:e=>{return M((t=he({},e),pe(t,ge({options:r}))));var t}}},document:{unstable_languageFilter:(e,{schemaType:r,schema:o})=>C(o.get(r),t)?[...e,n]:e},form:{components:{input:t=>"root"!==t.id&&f(t.schemaType)?function(t){const{options:n}=Q(),r=d(["_type"]);return C(p().get(r),n)?e(ce,ie({},t)):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));export{k as defaultFilterField,C as isLanguageFilterEnabled,ve as languageFilter,Q as useLanguageFilterStudioContext};//# sourceMappingURL=index.esm.js.map
3
+ `;function W(){const{options:i}=E(),l=i.supportedLanguages.filter((e=>i.defaultLanguages?.includes(e.id))),o=i.supportedLanguages.filter((e=>!i.defaultLanguages?.includes(e.id))),[d,c]=a(!1),{activeLanguages:g,allSelected:p,selectAll:f,selectNone:y,toggleLanguage:S}=function(){const{selectedLanguageIds:e,setSelectedLanguageIds:t,options:n}=E(),{defaultLanguages:a=[]}=n,i=r((()=>V(n)),[n]),l=s((e=>{var n;t(H([...a,...e])),n=H([...a,...e]),window.localStorage.setItem(O,JSON.stringify(n))}),[a,t]),o=s((()=>l(i.map((e=>e.id)))),[l,i]),d=s((()=>{l(a)}),[a,l]),u=s((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):H([...n,t]),l(n)}),[l,e]);return{activeLanguages:r((()=>H([...a??[],...e])),[a,e]),allSelected:e.length===i.length+a.length,selectAll:o,selectNone:d,toggleLanguage:u}}(),[F,N]=a(null),[j,D]=a(null),z=s((e=>{"ALL"===e.currentTarget.value?f():y()}),[f,y]),J=s((()=>c((e=>!e))),[]),$=s((()=>c(!1)),[]);T($,[F,j]);const _=i.supportedLanguages.length,[W,B]=a(""),G=s((e=>{e.currentTarget.value?B(e.currentTarget.value):B("")}),[]),K=_>4,M=/* @__PURE__ */e(P,{overflow:"auto",children:/* @__PURE__ */t(b,{padding:1,space:1,children:[l.length>0&&/* @__PURE__ */t(n,{children:[l.map((t=>/* @__PURE__ */e(q,{id:t.id,title:t.title,selected:!0},t.id))),
4
+ /* @__PURE__ */e(w,{borderTop:!0})]}),
5
+ /* @__PURE__ */e(A,{mode:"bleed",onClick:z,justify:"flex-start",value:p?"NONE":"ALL",disabled:!!W,children:/* @__PURE__ */t(x,{gap:3,align:"center",children:[
6
+ /* @__PURE__ */e(I,{size:2,children:p?/* @__PURE__ */e(u,{tone:"primary",children:/* @__PURE__ */e(m,{})}):/* @__PURE__ */e(h,{})}),
7
+ /* @__PURE__ */e(v,{flex:1,children:/* @__PURE__ */e(I,{children:p?"Hide all":"Show all"})})]})}),K?/* @__PURE__ */e(k,{onChange:G,value:W,placeholder:"Filter languages"}):/* @__PURE__ */e(w,{borderTop:!0}),o.filter((e=>!W||e.title.toLowerCase().includes(W.toLowerCase()))).map((t=>/* @__PURE__ */e(q,{id:t.id,onToggle:S,selected:g.includes(t.id),title:t.title},t.id)))]})}),Q=g.length===_?"Showing all":`Showing ${g.length} / ${_}`;/* @__PURE__ */
8
+ return e(C,{animate:!0,content:M,open:d,portal:!0,ref:D,children:/* @__PURE__ */e(A,{text:Q,icon:L,mode:"bleed",onClick:J,ref:N,selected:d})})}function q(n){const{id:a,onToggle:i,selected:l,title:r}=n,o=s((()=>{i&&i(a)}),[a,i]),d=!i;/* @__PURE__ */
9
+ return e(A,{mode:"bleed",onClick:o,justify:"flex-start",disabled:d,children:/* @__PURE__ */t(x,{gap:3,align:"center",children:[
10
+ /* @__PURE__ */e(I,{size:2,children:l?/* @__PURE__ */e(u,{tone:d?"default":"positive",children:/* @__PURE__ */e(y,{})}):/* @__PURE__ */e(S,{})}),
11
+ /* @__PURE__ */e(v,{flex:1,children:/* @__PURE__ */e(I,{children:r})}),
12
+ /* @__PURE__ */e(F,{children:a})]})})}function B(e){const{members:t,schemaType:n,renderDefault:a,...i}=e,{selectedLanguageIds:l,options:o}=E(),{filterField:s}=o,d=e.value;return a({...i,members:r((()=>t.filter((e=>"field"===e.kind&&s(n,e,l,d)||"fieldSet"===e.kind||"error"===e.kind)).map((e=>"fieldSet"===e.kind?{...e,fieldSet:{...e.fieldSet,members:e.fieldSet.members.filter((e=>"field"===e.kind&&s(n,e,l,d)))}}:e))),[n,t,s,l,d]),schemaType:n,renderDefault:a})}const G=p((t=>{const n=()=>/* @__PURE__ */e(W,{}),i={...$.options,...t};return{name:"@sanity/language-filter",studio:{components:{layout:t=>function(t){const n=d({apiVersion:"2023-01-01"}),[i,o]=a(Array.isArray(t.options.supportedLanguages)?t.options.supportedLanguages:[]);l((()=>{let e=[];Array.isArray(t.options.supportedLanguages)||async function(t){e=await t(n,{}),o(e)}(t.options.supportedLanguages)}),[n,t.options.supportedLanguages]);const s=r((()=>({...$.options,...t.options,supportedLanguages:i})),[t.options,i]),[u,c]=function(e){return a((()=>[...e.defaultLanguages??[],...J(e)]))}(s);/* @__PURE__ */
13
+ return e(_.Provider,{value:{options:s,selectedLanguageIds:u,setSelectedLanguageIds:c},children:t.renderDefault(t)})}({...t,options:i})}},document:{unstable_languageFilter:(e,{schemaType:a,schema:i})=>D(i.get(a),t)?[...e,n]:e},form:{components:{input:t=>"root"!==t.id&&f(t.schemaType)?function(t){const{options:n}=E(),a=c(["_type"]);return D(g().get(a),n)?/* @__PURE__ */e(B,{...t}):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));export{j as defaultFilterField,D as isLanguageFilterEnabled,G as languageFilter,E as useLanguageFilterStudioContext};//# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/filterField.ts","../src/useSelectedLanguageIds.ts","../src/LanguageFilterStudioContext.tsx","../src/usePaneLanguages.ts","../src/LanguageFilterMenuButton.tsx","../src/LanguageFilterObjectInput.tsx","../src/plugin.tsx"],"sourcesContent":["import type {SchemaType} from 'sanity'\n\nimport type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'\n\nexport const defaultFilterField: FilterFieldFunction = (\n enclosingType,\n field,\n selectedLanguageIds,\n) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)\n\nexport function isLanguageFilterEnabled(\n schemaType: SchemaType | undefined,\n options: LanguageFilterConfig,\n): boolean {\n const schemaFilter =\n isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter\n const defaultEnabled = !options.documentTypes\n\n return !!(\n (defaultEnabled && schemaFilter !== false) ||\n (!defaultEnabled && schemaFilter) ||\n (schemaType && options.documentTypes?.includes(schemaType.name))\n )\n}\n\nfunction isDocument(schemaType?: SchemaType) {\n return schemaType?.jsonType === 'object' && getRootType(schemaType).name === 'document'\n}\n\nfunction getRootType(schema: SchemaType): SchemaType {\n if (schema.type) {\n return getRootType(schema.type)\n }\n return schema\n}\n","import {useState} from 'react'\n\nimport type {Language, LanguageFilterConfig} from './types'\nconst storageKey = '@sanity/plugin/language-filter/selected-languages'\n\nexport function getPersistedLanguageIds(options: LanguageFilterConfig): string[] {\n const selectableLangs = getSelectableLanguages(options).map((l) => l.id)\n\n let selected: string[] = selectableLangs\n try {\n const persistedValue = window.localStorage.getItem(storageKey)\n if (persistedValue) {\n selected = JSON.parse(persistedValue)\n }\n } catch (err) {} // eslint-disable-line no-empty\n\n // constrain persisted/selected languages to the ones currently supported\n selected = intersection(selected, selectableLangs)\n return selected\n}\n\nexport function persistLanguageIds(languageIds: string[]): void {\n window.localStorage.setItem(storageKey, JSON.stringify(languageIds))\n}\n\nfunction intersection(array1: string[], array2: string[]) {\n return array1.filter((value) => array2.includes(value))\n}\n\nexport function getSelectableLanguages({\n supportedLanguages,\n defaultLanguages,\n}: LanguageFilterConfig): Language[] {\n return Array.isArray(supportedLanguages)\n ? supportedLanguages.filter((lang) => !defaultLanguages?.includes(lang.id))\n : []\n}\n\nexport function useSelectedLanguageIds(\n options: LanguageFilterConfig,\n): [string[], (ids: string[]) => void] {\n return useState(() => [...(options.defaultLanguages ?? []), ...getPersistedLanguageIds(options)])\n}\n","import {createContext, useContext, useEffect, useMemo, useState} from 'react'\nimport {type LayoutProps, useClient} from 'sanity'\n\nimport {defaultFilterField} from './filterField'\nimport type {\n Language,\n LanguageCallback,\n LanguageFilterConfig,\n LanguageFilterConfigProcessed,\n} from './types'\nimport {useSelectedLanguageIds} from './useSelectedLanguageIds'\n\nexport interface LanguageFilterStudioContextProps {\n // eslint-disable-next-line react/require-default-props\n options: Required<LanguageFilterConfig>\n}\n\nexport interface LanguageFilterStudioContextProcessed {\n options: Required<LanguageFilterConfigProcessed>\n}\n\nexport interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {\n selectedLanguageIds: string[]\n setSelectedLanguageIds: (ids: string[]) => void\n}\n\nexport const defaultContextValue: LanguageFilterStudioContextValue = {\n options: {\n apiVersion: '2022-11-27',\n supportedLanguages: [],\n defaultLanguages: [],\n documentTypes: [],\n filterField: defaultFilterField,\n },\n selectedLanguageIds: [],\n setSelectedLanguageIds: () => console.error('LanguageFilterStudioContext not initialized'),\n}\n\nconst LanguageFilterStudioContext =\n createContext<LanguageFilterStudioContextValue>(defaultContextValue)\n\n/**\n * This is a separate Provider from the Context that wraps the document pane\n * but it used to listen to changes to the selected language IDs inside it\n * and provide them to a Studio-wide context\n */\nexport function LanguageFilterStudioProvider(\n props: LayoutProps & LanguageFilterStudioContextProps,\n) {\n const client = useClient({apiVersion: '2023-01-01'})\n const [languages, setLanguages] = useState<Language[]>(\n Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],\n )\n useEffect(() => {\n let asyncLanguages: Language[] = []\n\n async function getLanguages(supportedLanguagesCallback: LanguageCallback) {\n asyncLanguages = await supportedLanguagesCallback(client, {})\n setLanguages(asyncLanguages)\n }\n\n if (!Array.isArray(props.options.supportedLanguages)) {\n getLanguages(props.options.supportedLanguages)\n }\n }, [client, props.options.supportedLanguages])\n\n const options = useMemo<Required<LanguageFilterConfigProcessed>>(() => {\n return {\n ...defaultContextValue.options,\n ...props.options,\n supportedLanguages: languages,\n }\n }, [props.options, languages])\n\n const [selectedLanguageIds, setSelectedLanguageIds] = useSelectedLanguageIds(options)\n\n return (\n <LanguageFilterStudioContext.Provider\n value={{options, selectedLanguageIds, setSelectedLanguageIds}}\n >\n {props.renderDefault(props)}\n </LanguageFilterStudioContext.Provider>\n )\n}\n\n/**\n * Retrieves plugin options and the currently selected\n * language IDs from anywhere in the Studio\n */\nexport function useLanguageFilterStudioContext() {\n return useContext(LanguageFilterStudioContext)\n}\n","import {useCallback, useMemo} from 'react'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'\n\nconst unique = (arr: string[]) => Array.from(new Set(arr))\n\nexport function usePaneLanguages(): {\n activeLanguages: string[]\n allSelected: boolean\n selectAll: () => void\n selectNone: () => void\n toggleLanguage: (languageId: string) => void\n} {\n const {selectedLanguageIds, setSelectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {defaultLanguages = []} = options\n\n const selectableLanguages = useMemo(() => getSelectableLanguages(options), [options])\n\n const updateSelectedIds = useCallback(\n (ids: string[]) => {\n setSelectedLanguageIds(unique([...defaultLanguages, ...ids]))\n persistLanguageIds(unique([...defaultLanguages, ...ids]))\n },\n [defaultLanguages, setSelectedLanguageIds],\n )\n\n const selectAll = useCallback(\n () => updateSelectedIds(selectableLanguages.map((l) => l.id)),\n [updateSelectedIds, selectableLanguages],\n )\n\n const selectNone = useCallback(() => {\n updateSelectedIds(defaultLanguages)\n }, [defaultLanguages, updateSelectedIds])\n\n const toggleLanguage = useCallback(\n (languageId: string) => {\n let lang = selectedLanguageIds\n\n if (lang.includes(languageId)) {\n lang = lang.filter((l) => l !== languageId)\n } else {\n lang = unique([...lang, languageId])\n }\n\n updateSelectedIds(lang)\n },\n [updateSelectedIds, selectedLanguageIds],\n )\n\n const activeLanguages = useMemo(\n () => unique([...(defaultLanguages ?? []), ...selectedLanguageIds]),\n [defaultLanguages, selectedLanguageIds],\n )\n\n return {\n activeLanguages,\n allSelected:\n selectedLanguageIds.length === selectableLanguages.length + defaultLanguages.length,\n selectAll,\n selectNone,\n toggleLanguage,\n }\n}\n","import {\n CheckmarkCircleIcon,\n CircleIcon,\n EyeClosedIcon,\n EyeOpenIcon,\n TranslateIcon,\n} from '@sanity/icons'\nimport {\n Badge,\n Box,\n Button,\n Card,\n Flex,\n Popover,\n Stack,\n Text,\n TextInput,\n useClickOutside,\n} from '@sanity/ui'\nimport {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'\nimport {TextWithTone} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {usePaneLanguages} from './usePaneLanguages'\n\nconst StyledBox = styled(Box)`\n max-height: calc(100vh - 200px);\n`\n\nexport function LanguageFilterMenuButton() {\n const {options} = useLanguageFilterStudioContext()\n\n const defaultLanguages = options.supportedLanguages.filter((l) =>\n options.defaultLanguages?.includes(l.id),\n )\n\n const languageOptions = options.supportedLanguages.filter(\n (l) => !options.defaultLanguages?.includes(l.id),\n )\n const [open, setOpen] = useState(false)\n const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()\n const [button, setButton] = useState<HTMLElement | null>(null)\n const [popover, setPopover] = useState<HTMLElement | null>(null)\n\n const handleToggleAll: MouseEventHandler<HTMLButtonElement> = useCallback(\n (event) => {\n const checked = event.currentTarget.value === 'ALL'\n\n if (checked) {\n selectAll()\n } else {\n selectNone()\n }\n },\n [selectAll, selectNone],\n )\n\n const handleClick = useCallback(() => setOpen((o) => !o), [])\n\n const handleClickOutside = useCallback(() => setOpen(false), [])\n\n useClickOutside(handleClickOutside, [button, popover])\n\n const langCount = options.supportedLanguages.length\n\n // Search filter query\n const [query, setQuery] = useState('')\n const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {\n if (event.currentTarget.value) {\n setQuery(event.currentTarget.value)\n } else {\n setQuery('')\n }\n }, [])\n\n const showSearch = langCount > 4\n\n const content = (\n <StyledBox overflow=\"auto\">\n <Stack padding={1} space={1}>\n {defaultLanguages.length > 0 && (\n <>\n {defaultLanguages.map((l) => (\n <LanguageFilterOption key={l.id} id={l.id} title={l.title} selected />\n ))}\n <Card borderTop />\n </>\n )}\n\n <Button\n mode=\"bleed\"\n onClick={handleToggleAll}\n justify=\"flex-start\"\n value={allSelected ? 'NONE' : 'ALL'}\n disabled={!!query}\n >\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {allSelected ? (\n <TextWithTone tone=\"primary\">\n <EyeClosedIcon />\n </TextWithTone>\n ) : (\n <EyeOpenIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{allSelected ? 'Hide all' : 'Show all'}</Text>\n </Box>\n </Flex>\n </Button>\n\n {showSearch ? (\n <TextInput onChange={handleQuery} value={query} placeholder=\"Filter languages\" />\n ) : (\n <Card borderTop />\n )}\n\n {languageOptions\n .filter((language) => {\n if (query) {\n return language.title.toLowerCase().includes(query.toLowerCase())\n }\n return true\n })\n .map((lang) => (\n <LanguageFilterOption\n id={lang.id}\n key={lang.id}\n onToggle={toggleLanguage}\n selected={activeLanguages.includes(lang.id)}\n title={lang.title}\n />\n ))}\n </Stack>\n </StyledBox>\n )\n\n const buttonText =\n activeLanguages.length === langCount\n ? 'Showing all'\n : `Showing ${activeLanguages.length} / ${langCount}`\n return (\n <Popover animate content={content} open={open} portal ref={setPopover}>\n <Button\n text={buttonText}\n icon={TranslateIcon}\n mode=\"bleed\"\n onClick={handleClick}\n ref={setButton}\n selected={open}\n />\n </Popover>\n )\n}\n\nfunction LanguageFilterOption(props: {\n id: string\n selected: boolean\n title: string\n // eslint-disable-next-line react/require-default-props\n onToggle?: (id: string) => void\n}) {\n const {id, onToggle, selected, title} = props\n\n const handleChange = useCallback(() => {\n if (onToggle) {\n onToggle(id)\n }\n }, [id, onToggle])\n\n const disabled = !onToggle\n\n return (\n <Button mode=\"bleed\" onClick={handleChange} justify=\"flex-start\" disabled={disabled}>\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {selected ? (\n <TextWithTone tone={disabled ? 'default' : 'positive'}>\n <CheckmarkCircleIcon />\n </TextWithTone>\n ) : (\n <CircleIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{title}</Text>\n </Box>\n <Badge>{id}</Badge>\n </Flex>\n </Button>\n )\n}\n","import {useMemo} from 'react'\nimport {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\n\n// First check that this Object is in a schema type for which language-filter is enabled\nexport function FilteredObjectWrapper(props: ObjectInputProps) {\n const {options} = useLanguageFilterStudioContext()\n\n const documentType = useFormValue(['_type']) as string\n const schema = useSchema()\n const languageFilterEnabled = isLanguageFilterEnabled(schema.get(documentType), options)\n return languageFilterEnabled ? <FilteredObjectInput {...props} /> : props.renderDefault(props)\n}\n\n// Modify the object members based on selected languages in the filter\nexport function FilteredObjectInput(props: ObjectInputProps) {\n const {members: membersProp, schemaType, renderDefault, ...restProps} = props\n const {selectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {filterField} = options\n\n const members: ObjectMember[] = useMemo(() => {\n return membersProp\n .filter((member) => {\n return (\n (member.kind === 'field' && filterField(schemaType, member, selectedLanguageIds)) ||\n member.kind === 'fieldSet' ||\n member.kind === 'error'\n )\n })\n .map((member) => {\n if (member.kind === 'fieldSet') {\n return {\n ...member,\n fieldSet: {\n ...member.fieldSet,\n members: member.fieldSet.members.filter((fieldsetMember) => {\n return (\n fieldsetMember.kind === 'field' &&\n filterField(schemaType, fieldsetMember, selectedLanguageIds)\n )\n }),\n },\n }\n }\n return member\n })\n }, [schemaType, membersProp, filterField, selectedLanguageIds])\n\n return renderDefault({...restProps, members, schemaType, renderDefault})\n}\n","import {\n definePlugin,\n type DocumentLanguageFilterComponent,\n isObjectSchemaType,\n type ObjectInputProps,\n} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {LanguageFilterMenuButton} from './LanguageFilterMenuButton'\nimport {FilteredObjectWrapper} from './LanguageFilterObjectInput'\nimport {defaultContextValue, LanguageFilterStudioProvider} from './LanguageFilterStudioContext'\nimport type {LanguageFilterConfig} from './types'\n\n/**\n * ## Usage in sanity.config.ts (or .js)\n *\n * ```\n * import {defineConfig} from 'sanity'\n * import {languageFilter} from '@sanity/language-filter'\n *\n * export const defineConfig({\n * /...\n * plugins: [\n * languageFilter({\n * supportedLanguages: [\n * {id: 'nb', title: 'Norwegian (Bokmål)'},\n * {id: 'nn', title: 'Norwegian (Nynorsk)'},\n * {id: 'en', title: 'English'},\n * {id: 'es', title: 'Spanish'},\n * {id: 'arb', title: 'Arabic'},\n * {id: 'pt', title: 'Portuguese'},\n * //...\n * ],\n * // Select Norwegian (Bokmål) by default\n * defaultLanguages: ['nb'],\n * // Only show language filter for document type `page` (schemaType.name)\n * // Can also enable via document-options: options.languageFilter: true\n * documentTypes: ['page'],\n * // default filter function shown\n * filterField: (enclosingType, field, selectedLanguageIds) =>\n * !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name),\n * })\n * ]\n * })\n * ```\n */\nexport const languageFilter = definePlugin<LanguageFilterConfig>((options) => {\n const RenderLanguageFilter: DocumentLanguageFilterComponent = () => {\n return <LanguageFilterMenuButton />\n }\n\n const pluginOptions = {\n ...defaultContextValue.options,\n ...options,\n }\n\n return {\n name: '@sanity/language-filter',\n studio: {\n components: {\n layout: (props) => LanguageFilterStudioProvider({...props, options: pluginOptions}),\n },\n },\n\n document: {\n unstable_languageFilter: (prev, {schemaType, schema}) => {\n if (isLanguageFilterEnabled(schema.get(schemaType), options)) {\n return [...prev, RenderLanguageFilter]\n }\n return prev\n },\n },\n\n form: {\n components: {\n input: (props) => {\n if (props.id !== 'root' && isObjectSchemaType(props.schemaType)) {\n return FilteredObjectWrapper(props as ObjectInputProps)\n }\n\n return props.renderDefault(props)\n },\n },\n },\n }\n})\n"],"names":["defaultFilterField","enclosingType","field","selectedLanguageIds","name","startsWith","includes","isLanguageFilterEnabled","schemaType","options","_a","_b","schemaFilter","jsonType","getRootType","isDocument","languageFilter","defaultEnabled","documentTypes","schema","type","storageKey","getPersistedLanguageIds","selectableLangs","getSelectableLanguages","map","l","id","selected","persistedValue","window","localStorage","getItem","JSON","parse","err","array2","filter","value","supportedLanguages","defaultLanguages","Array","isArray","lang","defaultContextValue","apiVersion","filterField","setSelectedLanguageIds","console","error","LanguageFilterStudioContext","createContext","LanguageFilterStudioProvider","props","client","useClient","languages","setLanguages","useState","useEffect","asyncLanguages","async","supportedLanguagesCallback","getLanguages","useMemo","__spreadProps","useSelectedLanguageIds","jsx","Provider","children","renderDefault","useLanguageFilterStudioContext","useContext","unique","arr","from","Set","usePaneLanguages","selectableLanguages","updateSelectedIds","useCallback","ids","languageIds","setItem","stringify","selectAll","selectNone","toggleLanguage","languageId","activeLanguages","allSelected","length","StyledBox","styled","Box","LanguageFilterMenuButton","languageOptions","open","setOpen","button","setButton","popover","setPopover","handleToggleAll","event","currentTarget","handleClick","o","handleClickOutside","useClickOutside","langCount","query","setQuery","handleQuery","showSearch","content","overflow","jsxs","Stack","padding","space","Fragment","LanguageFilterOption","title","Card","borderTop","Button","mode","onClick","justify","disabled","Flex","gap","align","Text","size","TextWithTone","tone","EyeClosedIcon","EyeOpenIcon","flex","TextInput","onChange","placeholder","language","toLowerCase","onToggle","buttonText","Popover","animate","portal","ref","text","icon","TranslateIcon","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","restProps","__objRest","member","kind","__spreadValues","fieldSet","fieldsetMember","definePlugin","RenderLanguageFilter","pluginOptions","studio","components","layout","document","unstable_languageFilter","prev","get","form","input","isObjectSchemaType","documentType","useFormValue","useSchema","FilteredObjectWrapper"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/filterField.ts","../src/useSelectedLanguageIds.ts","../src/LanguageFilterStudioContext.tsx","../src/usePaneLanguages.ts","../src/LanguageFilterMenuButton.tsx","../src/LanguageFilterObjectInput.tsx","../src/plugin.tsx"],"sourcesContent":["import type {SchemaType} from 'sanity'\n\nimport type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'\n\nexport const defaultFilterField: FilterFieldFunction = (\n enclosingType,\n field,\n selectedLanguageIds,\n) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)\n\nexport function isLanguageFilterEnabled(\n schemaType: SchemaType | undefined,\n options: LanguageFilterConfig,\n): boolean {\n const schemaFilter =\n isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter\n const defaultEnabled = !options.documentTypes\n\n return !!(\n (defaultEnabled && schemaFilter !== false) ||\n (!defaultEnabled && schemaFilter) ||\n (schemaType && options.documentTypes?.includes(schemaType.name))\n )\n}\n\nfunction isDocument(schemaType?: SchemaType) {\n return schemaType?.jsonType === 'object' && getRootType(schemaType).name === 'document'\n}\n\nfunction getRootType(schema: SchemaType): SchemaType {\n if (schema.type) {\n return getRootType(schema.type)\n }\n return schema\n}\n","import {useState} from 'react'\n\nimport type {Language, LanguageFilterConfig} from './types'\nconst storageKey = '@sanity/plugin/language-filter/selected-languages'\n\nexport function getPersistedLanguageIds(options: LanguageFilterConfig): string[] {\n const selectableLangs = getSelectableLanguages(options).map((l) => l.id)\n\n let selected: string[] = selectableLangs\n try {\n const persistedValue = window.localStorage.getItem(storageKey)\n if (persistedValue) {\n selected = JSON.parse(persistedValue)\n }\n } catch (err) {} // eslint-disable-line no-empty\n\n // constrain persisted/selected languages to the ones currently supported\n selected = intersection(selected, selectableLangs)\n return selected\n}\n\nexport function persistLanguageIds(languageIds: string[]): void {\n window.localStorage.setItem(storageKey, JSON.stringify(languageIds))\n}\n\nfunction intersection(array1: string[], array2: string[]) {\n return array1.filter((value) => array2.includes(value))\n}\n\nexport function getSelectableLanguages({\n supportedLanguages,\n defaultLanguages,\n}: LanguageFilterConfig): Language[] {\n return Array.isArray(supportedLanguages)\n ? supportedLanguages.filter((lang) => !defaultLanguages?.includes(lang.id))\n : []\n}\n\nexport function useSelectedLanguageIds(\n options: LanguageFilterConfig,\n): [string[], (ids: string[]) => void] {\n return useState(() => [...(options.defaultLanguages ?? []), ...getPersistedLanguageIds(options)])\n}\n","import {createContext, useContext, useEffect, useMemo, useState} from 'react'\nimport {type LayoutProps, useClient} from 'sanity'\n\nimport {defaultFilterField} from './filterField'\nimport type {\n Language,\n LanguageCallback,\n LanguageFilterConfig,\n LanguageFilterConfigProcessed,\n} from './types'\nimport {useSelectedLanguageIds} from './useSelectedLanguageIds'\n\nexport interface LanguageFilterStudioContextProps {\n // eslint-disable-next-line react/require-default-props\n options: Required<LanguageFilterConfig>\n}\n\nexport interface LanguageFilterStudioContextProcessed {\n options: Required<LanguageFilterConfigProcessed>\n}\n\nexport interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {\n selectedLanguageIds: string[]\n setSelectedLanguageIds: (ids: string[]) => void\n}\n\nexport const defaultContextValue: LanguageFilterStudioContextValue = {\n options: {\n apiVersion: '2022-11-27',\n supportedLanguages: [],\n defaultLanguages: [],\n documentTypes: [],\n filterField: defaultFilterField,\n },\n selectedLanguageIds: [],\n setSelectedLanguageIds: () => console.error('LanguageFilterStudioContext not initialized'),\n}\n\nconst LanguageFilterStudioContext =\n createContext<LanguageFilterStudioContextValue>(defaultContextValue)\n\n/**\n * This is a separate Provider from the Context that wraps the document pane\n * but it used to listen to changes to the selected language IDs inside it\n * and provide them to a Studio-wide context\n */\nexport function LanguageFilterStudioProvider(\n props: LayoutProps & LanguageFilterStudioContextProps,\n) {\n const client = useClient({apiVersion: '2023-01-01'})\n const [languages, setLanguages] = useState<Language[]>(\n Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],\n )\n useEffect(() => {\n let asyncLanguages: Language[] = []\n\n async function getLanguages(supportedLanguagesCallback: LanguageCallback) {\n asyncLanguages = await supportedLanguagesCallback(client, {})\n setLanguages(asyncLanguages)\n }\n\n if (!Array.isArray(props.options.supportedLanguages)) {\n getLanguages(props.options.supportedLanguages)\n }\n }, [client, props.options.supportedLanguages])\n\n const options = useMemo<Required<LanguageFilterConfigProcessed>>(() => {\n return {\n ...defaultContextValue.options,\n ...props.options,\n supportedLanguages: languages,\n }\n }, [props.options, languages])\n\n const [selectedLanguageIds, setSelectedLanguageIds] = useSelectedLanguageIds(options)\n\n return (\n <LanguageFilterStudioContext.Provider\n value={{options, selectedLanguageIds, setSelectedLanguageIds}}\n >\n {props.renderDefault(props)}\n </LanguageFilterStudioContext.Provider>\n )\n}\n\n/**\n * Retrieves plugin options and the currently selected\n * language IDs from anywhere in the Studio\n */\nexport function useLanguageFilterStudioContext() {\n return useContext(LanguageFilterStudioContext)\n}\n","import {useCallback, useMemo} from 'react'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'\n\nconst unique = (arr: string[]) => Array.from(new Set(arr))\n\nexport function usePaneLanguages(): {\n activeLanguages: string[]\n allSelected: boolean\n selectAll: () => void\n selectNone: () => void\n toggleLanguage: (languageId: string) => void\n} {\n const {selectedLanguageIds, setSelectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {defaultLanguages = []} = options\n\n const selectableLanguages = useMemo(() => getSelectableLanguages(options), [options])\n\n const updateSelectedIds = useCallback(\n (ids: string[]) => {\n setSelectedLanguageIds(unique([...defaultLanguages, ...ids]))\n persistLanguageIds(unique([...defaultLanguages, ...ids]))\n },\n [defaultLanguages, setSelectedLanguageIds],\n )\n\n const selectAll = useCallback(\n () => updateSelectedIds(selectableLanguages.map((l) => l.id)),\n [updateSelectedIds, selectableLanguages],\n )\n\n const selectNone = useCallback(() => {\n updateSelectedIds(defaultLanguages)\n }, [defaultLanguages, updateSelectedIds])\n\n const toggleLanguage = useCallback(\n (languageId: string) => {\n let lang = selectedLanguageIds\n\n if (lang.includes(languageId)) {\n lang = lang.filter((l) => l !== languageId)\n } else {\n lang = unique([...lang, languageId])\n }\n\n updateSelectedIds(lang)\n },\n [updateSelectedIds, selectedLanguageIds],\n )\n\n const activeLanguages = useMemo(\n () => unique([...(defaultLanguages ?? []), ...selectedLanguageIds]),\n [defaultLanguages, selectedLanguageIds],\n )\n\n return {\n activeLanguages,\n allSelected:\n selectedLanguageIds.length === selectableLanguages.length + defaultLanguages.length,\n selectAll,\n selectNone,\n toggleLanguage,\n }\n}\n","import {\n CheckmarkCircleIcon,\n CircleIcon,\n EyeClosedIcon,\n EyeOpenIcon,\n TranslateIcon,\n} from '@sanity/icons'\nimport {\n Badge,\n Box,\n Button,\n Card,\n Flex,\n Popover,\n Stack,\n Text,\n TextInput,\n useClickOutside,\n} from '@sanity/ui'\nimport {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'\nimport {TextWithTone} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {usePaneLanguages} from './usePaneLanguages'\n\nconst StyledBox = styled(Box)`\n max-height: calc(100vh - 200px);\n`\n\nexport function LanguageFilterMenuButton() {\n const {options} = useLanguageFilterStudioContext()\n\n const defaultLanguages = options.supportedLanguages.filter((l) =>\n options.defaultLanguages?.includes(l.id),\n )\n\n const languageOptions = options.supportedLanguages.filter(\n (l) => !options.defaultLanguages?.includes(l.id),\n )\n const [open, setOpen] = useState(false)\n const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()\n const [button, setButton] = useState<HTMLElement | null>(null)\n const [popover, setPopover] = useState<HTMLElement | null>(null)\n\n const handleToggleAll: MouseEventHandler<HTMLButtonElement> = useCallback(\n (event) => {\n const checked = event.currentTarget.value === 'ALL'\n\n if (checked) {\n selectAll()\n } else {\n selectNone()\n }\n },\n [selectAll, selectNone],\n )\n\n const handleClick = useCallback(() => setOpen((o) => !o), [])\n\n const handleClickOutside = useCallback(() => setOpen(false), [])\n\n useClickOutside(handleClickOutside, [button, popover])\n\n const langCount = options.supportedLanguages.length\n\n // Search filter query\n const [query, setQuery] = useState('')\n const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {\n if (event.currentTarget.value) {\n setQuery(event.currentTarget.value)\n } else {\n setQuery('')\n }\n }, [])\n\n const showSearch = langCount > 4\n\n const content = (\n <StyledBox overflow=\"auto\">\n <Stack padding={1} space={1}>\n {defaultLanguages.length > 0 && (\n <>\n {defaultLanguages.map((l) => (\n <LanguageFilterOption key={l.id} id={l.id} title={l.title} selected />\n ))}\n <Card borderTop />\n </>\n )}\n\n <Button\n mode=\"bleed\"\n onClick={handleToggleAll}\n justify=\"flex-start\"\n value={allSelected ? 'NONE' : 'ALL'}\n disabled={!!query}\n >\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {allSelected ? (\n <TextWithTone tone=\"primary\">\n <EyeClosedIcon />\n </TextWithTone>\n ) : (\n <EyeOpenIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{allSelected ? 'Hide all' : 'Show all'}</Text>\n </Box>\n </Flex>\n </Button>\n\n {showSearch ? (\n <TextInput onChange={handleQuery} value={query} placeholder=\"Filter languages\" />\n ) : (\n <Card borderTop />\n )}\n\n {languageOptions\n .filter((language) => {\n if (query) {\n return language.title.toLowerCase().includes(query.toLowerCase())\n }\n return true\n })\n .map((lang) => (\n <LanguageFilterOption\n id={lang.id}\n key={lang.id}\n onToggle={toggleLanguage}\n selected={activeLanguages.includes(lang.id)}\n title={lang.title}\n />\n ))}\n </Stack>\n </StyledBox>\n )\n\n const buttonText =\n activeLanguages.length === langCount\n ? 'Showing all'\n : `Showing ${activeLanguages.length} / ${langCount}`\n return (\n <Popover animate content={content} open={open} portal ref={setPopover}>\n <Button\n text={buttonText}\n icon={TranslateIcon}\n mode=\"bleed\"\n onClick={handleClick}\n ref={setButton}\n selected={open}\n />\n </Popover>\n )\n}\n\nfunction LanguageFilterOption(props: {\n id: string\n selected: boolean\n title: string\n // eslint-disable-next-line react/require-default-props\n onToggle?: (id: string) => void\n}) {\n const {id, onToggle, selected, title} = props\n\n const handleChange = useCallback(() => {\n if (onToggle) {\n onToggle(id)\n }\n }, [id, onToggle])\n\n const disabled = !onToggle\n\n return (\n <Button mode=\"bleed\" onClick={handleChange} justify=\"flex-start\" disabled={disabled}>\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {selected ? (\n <TextWithTone tone={disabled ? 'default' : 'positive'}>\n <CheckmarkCircleIcon />\n </TextWithTone>\n ) : (\n <CircleIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{title}</Text>\n </Box>\n <Badge>{id}</Badge>\n </Flex>\n </Button>\n )\n}\n","import {useMemo} from 'react'\nimport {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\n\n// First check that this Object is in a schema type for which language-filter is enabled\nexport function FilteredObjectWrapper(props: ObjectInputProps) {\n const {options} = useLanguageFilterStudioContext()\n\n const documentType = useFormValue(['_type']) as string\n const schema = useSchema()\n const languageFilterEnabled = isLanguageFilterEnabled(schema.get(documentType), options)\n return languageFilterEnabled ? <FilteredObjectInput {...props} /> : props.renderDefault(props)\n}\n\n// Modify the object members based on selected languages in the filter\nexport function FilteredObjectInput(props: ObjectInputProps) {\n const {members: membersProp, schemaType, renderDefault, ...restProps} = props\n const {selectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {filterField} = options\n const parentValue = props.value\n\n const members: ObjectMember[] = useMemo(() => {\n return membersProp\n .filter((member) => {\n return (\n (member.kind === 'field' &&\n filterField(schemaType, member, selectedLanguageIds, parentValue)) ||\n member.kind === 'fieldSet' ||\n member.kind === 'error'\n )\n })\n .map((member) => {\n if (member.kind === 'fieldSet') {\n return {\n ...member,\n fieldSet: {\n ...member.fieldSet,\n members: member.fieldSet.members.filter((fieldsetMember) => {\n return (\n fieldsetMember.kind === 'field' &&\n filterField(schemaType, fieldsetMember, selectedLanguageIds, parentValue)\n )\n }),\n },\n }\n }\n return member\n })\n }, [schemaType, membersProp, filterField, selectedLanguageIds, parentValue])\n\n return renderDefault({...restProps, members, schemaType, renderDefault})\n}\n","import {\n definePlugin,\n type DocumentLanguageFilterComponent,\n isObjectSchemaType,\n type ObjectInputProps,\n} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {LanguageFilterMenuButton} from './LanguageFilterMenuButton'\nimport {FilteredObjectWrapper} from './LanguageFilterObjectInput'\nimport {defaultContextValue, LanguageFilterStudioProvider} from './LanguageFilterStudioContext'\nimport type {LanguageFilterConfig} from './types'\n\n/**\n * ## Usage in sanity.config.ts (or .js)\n *\n * ```\n * import {defineConfig} from 'sanity'\n * import {languageFilter} from '@sanity/language-filter'\n *\n * export const defineConfig({\n * /...\n * plugins: [\n * languageFilter({\n * supportedLanguages: [\n * {id: 'nb', title: 'Norwegian (Bokmål)'},\n * {id: 'nn', title: 'Norwegian (Nynorsk)'},\n * {id: 'en', title: 'English'},\n * {id: 'es', title: 'Spanish'},\n * {id: 'arb', title: 'Arabic'},\n * {id: 'pt', title: 'Portuguese'},\n * //...\n * ],\n * // Select Norwegian (Bokmål) by default\n * defaultLanguages: ['nb'],\n * // Only show language filter for document type `page` (schemaType.name)\n * // Can also enable via document-options: options.languageFilter: true\n * documentTypes: ['page'],\n * // default filter function shown\n * filterField: (enclosingType, field, selectedLanguageIds) =>\n * !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name),\n * })\n * ]\n * })\n * ```\n */\nexport const languageFilter = definePlugin<LanguageFilterConfig>((options) => {\n const RenderLanguageFilter: DocumentLanguageFilterComponent = () => {\n return <LanguageFilterMenuButton />\n }\n\n const pluginOptions = {\n ...defaultContextValue.options,\n ...options,\n }\n\n return {\n name: '@sanity/language-filter',\n studio: {\n components: {\n layout: (props) => LanguageFilterStudioProvider({...props, options: pluginOptions}),\n },\n },\n\n document: {\n unstable_languageFilter: (prev, {schemaType, schema}) => {\n if (isLanguageFilterEnabled(schema.get(schemaType), options)) {\n return [...prev, RenderLanguageFilter]\n }\n return prev\n },\n },\n\n form: {\n components: {\n input: (props) => {\n if (props.id !== 'root' && isObjectSchemaType(props.schemaType)) {\n return FilteredObjectWrapper(props as ObjectInputProps)\n }\n\n return props.renderDefault(props)\n },\n },\n },\n }\n})\n"],"names":["defaultFilterField","enclosingType","field","selectedLanguageIds","name","startsWith","includes","isLanguageFilterEnabled","schemaType","options","schemaFilter","jsonType","getRootType","isDocument","languageFilter","defaultEnabled","documentTypes","schema","type","storageKey","getPersistedLanguageIds","selectableLangs","getSelectableLanguages","map","l","id","selected","persistedValue","window","localStorage","getItem","JSON","parse","array2","filter","value","supportedLanguages","defaultLanguages","Array","isArray","lang","defaultContextValue","apiVersion","filterField","setSelectedLanguageIds","console","error","LanguageFilterStudioContext","createContext","useLanguageFilterStudioContext","useContext","unique","arr","from","Set","StyledBox","styled","Box","LanguageFilterMenuButton","languageOptions","open","setOpen","useState","activeLanguages","allSelected","selectAll","selectNone","toggleLanguage","selectableLanguages","useMemo","updateSelectedIds","useCallback","ids","languageIds","setItem","stringify","languageId","length","usePaneLanguages","button","setButton","popover","setPopover","handleToggleAll","event","currentTarget","handleClick","o","handleClickOutside","useClickOutside","langCount","query","setQuery","handleQuery","showSearch","content","jsx","overflow","children","jsxs","Stack","padding","space","Fragment","LanguageFilterOption","title","Card","borderTop","Button","mode","onClick","justify","disabled","Flex","gap","align","Text","size","TextWithTone","tone","EyeClosedIcon","EyeOpenIcon","flex","TextInput","onChange","placeholder","language","toLowerCase","onToggle","buttonText","Popover","animate","portal","ref","text","icon","TranslateIcon","props","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","renderDefault","restProps","parentValue","member","kind","fieldSet","fieldsetMember","definePlugin","RenderLanguageFilter","pluginOptions","studio","components","layout","client","useClient","languages","setLanguages","useEffect","asyncLanguages","async","supportedLanguagesCallback","getLanguages","useSelectedLanguageIds","Provider","LanguageFilterStudioProvider","document","unstable_languageFilter","prev","get","form","input","isObjectSchemaType","documentType","useFormValue","useSchema","FilteredObjectWrapper"],"mappings":"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"}
package/lib/index.js CHANGED
@@ -1,3 +1,13 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("react"),n=require("sanity"),r=require("@sanity/icons"),a=require("@sanity/ui"),l=require("styled-components");const s=(e,t,n)=>!e.name.startsWith("locale")||n.includes(t.name);function o(e,t){var n,r;const a=function(e){return"object"===(null==e?void 0:e.jsonType)&&"document"===i(e).name}(e)&&(null==(n=null==e?void 0:e.options)?void 0:n.languageFilter),l=!t.documentTypes;return!!(l&&!1!==a||!l&&a||e&&null!=(r=t.documentTypes)&&r.includes(e.name))}function i(e){return e.type?i(e.type):e}const u="@sanity/plugin/language-filter/selected-languages";function c(e){const t=d(e).map((e=>e.id));let n=t;try{const e=window.localStorage.getItem(u);e&&(n=JSON.parse(e))}catch(e){}return r=t,n=n.filter((e=>r.includes(e))),n;var r}function d({supportedLanguages:e,defaultLanguages:t}){return Array.isArray(e)?e.filter((e=>!(null!=t&&t.includes(e.id)))):[]}var g=Object.defineProperty,p=Object.defineProperties,f=Object.getOwnPropertyDescriptors,m=Object.getOwnPropertySymbols,x=Object.prototype.hasOwnProperty,y=Object.prototype.propertyIsEnumerable,b=(e,t,n)=>t in e?g(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,j=(e,t)=>{for(var n in t||(t={}))x.call(t,n)&&b(e,n,t[n]);if(m)for(var n of m(t))y.call(t,n)&&b(e,n,t[n]);return e};const h={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:s},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},v=t.createContext(h);function L(r){const a=n.useClient({apiVersion:"2023-01-01"}),[l,s]=t.useState(Array.isArray(r.options.supportedLanguages)?r.options.supportedLanguages:[]);t.useEffect((()=>{let e=[];Array.isArray(r.options.supportedLanguages)||async function(t){e=await t(a,{}),s(e)}(r.options.supportedLanguages)}),[a,r.options.supportedLanguages]);const o=t.useMemo((()=>{return e=j(j({},h.options),r.options),p(e,f({supportedLanguages:l}));var e}),[r.options,l]),[i,u]=function(e){return t.useState((()=>{var t;return[...null!=(t=e.defaultLanguages)?t:[],...c(e)]}))}(o);return e.jsx(v.Provider,{value:{options:o,selectedLanguageIds:i,setSelectedLanguageIds:u},children:r.renderDefault(r)})}function O(){return t.useContext(v)}const S=e=>Array.from(new Set(e));function C(){const{selectedLanguageIds:e,setSelectedLanguageIds:n,options:r}=O(),{defaultLanguages:a=[]}=r,l=t.useMemo((()=>d(r)),[r]),s=t.useCallback((e=>{var t;n(S([...a,...e])),t=S([...a,...e]),window.localStorage.setItem(u,JSON.stringify(t))}),[a,n]),o=t.useCallback((()=>s(l.map((e=>e.id)))),[s,l]),i=t.useCallback((()=>{s(a)}),[a,s]),c=t.useCallback((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):S([...n,t]),s(n)}),[s,e]);return{activeLanguages:t.useMemo((()=>S([...null!=a?a:[],...e])),[a,e]),allSelected:e.length===l.length+a.length,selectAll:o,selectNone:i,toggleLanguage:c}}const T=l.styled(a.Box)`
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("react"),n=require("sanity"),s=require("@sanity/icons"),a=require("@sanity/ui"),l=require("styled-components");const i=(e,t,n)=>!e.name.startsWith("locale")||n.includes(t.name);function r(e,t){const n=function(e){return"object"===e?.jsonType&&"document"===o(e).name}(e)&&e?.options?.languageFilter,s=!t.documentTypes;return!!(s&&!1!==n||!s&&n||e&&t.documentTypes?.includes(e.name))}function o(e){return e.type?o(e.type):e}const u="@sanity/plugin/language-filter/selected-languages";function c(e){const t=d(e).map((e=>e.id));let n=t;try{const e=window.localStorage.getItem(u);e&&(n=JSON.parse(e))}catch{}return s=t,n=n.filter((e=>s.includes(e))),n;var s}function d({supportedLanguages:e,defaultLanguages:t}){return Array.isArray(e)?e.filter((e=>!t?.includes(e.id))):[]}const g={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:i},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},p=t.createContext(g);function f(){return t.useContext(p)}const x=e=>Array.from(new Set(e)),m=l.styled(a.Box)`
2
2
  max-height: calc(100vh - 200px);
3
- `;function w(){const{options:l}=O(),s=l.supportedLanguages.filter((e=>{var t;return null==(t=l.defaultLanguages)?void 0:t.includes(e.id)})),o=l.supportedLanguages.filter((e=>{var t;return!(null!=(t=l.defaultLanguages)&&t.includes(e.id))})),[i,u]=t.useState(!1),{activeLanguages:c,allSelected:d,selectAll:g,selectNone:p,toggleLanguage:f}=C(),[m,x]=t.useState(null),[y,b]=t.useState(null),j=t.useCallback((e=>{"ALL"===e.currentTarget.value?g():p()}),[g,p]),h=t.useCallback((()=>u((e=>!e))),[]),v=t.useCallback((()=>u(!1)),[]);a.useClickOutside(v,[m,y]);const L=l.supportedLanguages.length,[S,w]=t.useState(""),P=t.useCallback((e=>{e.currentTarget.value?w(e.currentTarget.value):w("")}),[]),I=L>4,F=e.jsx(T,{overflow:"auto",children:e.jsxs(a.Stack,{padding:1,space:1,children:[s.length>0&&e.jsxs(e.Fragment,{children:[s.map((t=>e.jsx(k,{id:t.id,title:t.title,selected:!0},t.id))),e.jsx(a.Card,{borderTop:!0})]}),e.jsx(a.Button,{mode:"bleed",onClick:j,justify:"flex-start",value:d?"NONE":"ALL",disabled:!!S,children:e.jsxs(a.Flex,{gap:3,align:"center",children:[e.jsx(a.Text,{size:2,children:d?e.jsx(n.TextWithTone,{tone:"primary",children:e.jsx(r.EyeClosedIcon,{})}):e.jsx(r.EyeOpenIcon,{})}),e.jsx(a.Box,{flex:1,children:e.jsx(a.Text,{children:d?"Hide all":"Show all"})})]})}),I?e.jsx(a.TextInput,{onChange:P,value:S,placeholder:"Filter languages"}):e.jsx(a.Card,{borderTop:!0}),o.filter((e=>!S||e.title.toLowerCase().includes(S.toLowerCase()))).map((t=>e.jsx(k,{id:t.id,onToggle:f,selected:c.includes(t.id),title:t.title},t.id)))]})}),A=c.length===L?"Showing all":`Showing ${c.length} / ${L}`;return e.jsx(a.Popover,{animate:!0,content:F,open:i,portal:!0,ref:b,children:e.jsx(a.Button,{text:A,icon:r.TranslateIcon,mode:"bleed",onClick:h,ref:x,selected:i})})}function k(l){const{id:s,onToggle:o,selected:i,title:u}=l,c=t.useCallback((()=>{o&&o(s)}),[s,o]),d=!o;return e.jsx(a.Button,{mode:"bleed",onClick:c,justify:"flex-start",disabled:d,children:e.jsxs(a.Flex,{gap:3,align:"center",children:[e.jsx(a.Text,{size:2,children:i?e.jsx(n.TextWithTone,{tone:d?"default":"positive",children:e.jsx(r.CheckmarkCircleIcon,{})}):e.jsx(r.CircleIcon,{})}),e.jsx(a.Box,{flex:1,children:e.jsx(a.Text,{children:u})}),e.jsx(a.Badge,{children:s})]})})}var P=Object.defineProperty,I=Object.defineProperties,F=Object.getOwnPropertyDescriptors,A=Object.getOwnPropertySymbols,D=Object.prototype.hasOwnProperty,E=Object.prototype.propertyIsEnumerable,B=(e,t,n)=>t in e?P(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,q=(e,t)=>{for(var n in t||(t={}))D.call(t,n)&&B(e,n,t[n]);if(A)for(var n of A(t))E.call(t,n)&&B(e,n,t[n]);return e},N=(e,t)=>I(e,F(t)),M=(e,t)=>{var n={};for(var r in e)D.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&A)for(var r of A(e))t.indexOf(r)<0&&E.call(e,r)&&(n[r]=e[r]);return n};function _(e){const n=e,{members:r,schemaType:a,renderDefault:l}=n,s=M(n,["members","schemaType","renderDefault"]),{selectedLanguageIds:o,options:i}=O(),{filterField:u}=i,c=t.useMemo((()=>r.filter((e=>"field"===e.kind&&u(a,e,o)||"fieldSet"===e.kind||"error"===e.kind)).map((e=>"fieldSet"===e.kind?N(q({},e),{fieldSet:N(q({},e.fieldSet),{members:e.fieldSet.members.filter((e=>"field"===e.kind&&u(a,e,o)))})}):e))),[a,r,u,o]);return l(N(q({},s),{members:c,schemaType:a,renderDefault:l}))}var z=Object.defineProperty,V=Object.defineProperties,W=Object.getOwnPropertyDescriptors,J=Object.getOwnPropertySymbols,$=Object.prototype.hasOwnProperty,H=Object.prototype.propertyIsEnumerable,G=(e,t,n)=>t in e?z(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,K=(e,t)=>{for(var n in t||(t={}))$.call(t,n)&&G(e,n,t[n]);if(J)for(var n of J(t))H.call(t,n)&&G(e,n,t[n]);return e};const Q=n.definePlugin((t=>{const r=()=>e.jsx(w,{}),a=K(K({},h.options),t);return{name:"@sanity/language-filter",studio:{components:{layout:e=>{return L((t=K({},e),V(t,W({options:a}))));var t}}},document:{unstable_languageFilter:(e,{schemaType:n,schema:a})=>o(a.get(n),t)?[...e,r]:e},form:{components:{input:t=>"root"!==t.id&&n.isObjectSchemaType(t.schemaType)?function(t){const{options:r}=O(),a=n.useFormValue(["_type"]);return o(n.useSchema().get(a),r)?e.jsx(_,q({},t)):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));exports.defaultFilterField=s,exports.isLanguageFilterEnabled=o,exports.languageFilter=Q,exports.useLanguageFilterStudioContext=O;//# sourceMappingURL=index.js.map
3
+ `;function h(){const{options:l}=f(),i=l.supportedLanguages.filter((e=>l.defaultLanguages?.includes(e.id))),r=l.supportedLanguages.filter((e=>!l.defaultLanguages?.includes(e.id))),[o,c]=t.useState(!1),{activeLanguages:g,allSelected:p,selectAll:h,selectNone:L,toggleLanguage:y}=function(){const{selectedLanguageIds:e,setSelectedLanguageIds:n,options:s}=f(),{defaultLanguages:a=[]}=s,l=t.useMemo((()=>d(s)),[s]),i=t.useCallback((e=>{var t;n(x([...a,...e])),t=x([...a,...e]),window.localStorage.setItem(u,JSON.stringify(t))}),[a,n]),r=t.useCallback((()=>i(l.map((e=>e.id)))),[i,l]),o=t.useCallback((()=>{i(a)}),[a,i]),c=t.useCallback((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):x([...n,t]),i(n)}),[i,e]);return{activeLanguages:t.useMemo((()=>x([...a??[],...e])),[a,e]),allSelected:e.length===l.length+a.length,selectAll:r,selectNone:o,toggleLanguage:c}}(),[S,C]=t.useState(null),[T,b]=t.useState(null),k=t.useCallback((e=>{"ALL"===e.currentTarget.value?h():L()}),[h,L]),v=t.useCallback((()=>c((e=>!e))),[]),F=t.useCallback((()=>c(!1)),[]);a.useClickOutside(F,[S,T]);const I=l.supportedLanguages.length,[w,A]=t.useState(""),B=t.useCallback((e=>{e.currentTarget.value?A(e.currentTarget.value):A("")}),[]),O=I>4,q=/* @__PURE__ */e.jsx(m,{overflow:"auto",children:/* @__PURE__ */e.jsxs(a.Stack,{padding:1,space:1,children:[i.length>0&&/* @__PURE__ */e.jsxs(e.Fragment,{children:[i.map((t=>/* @__PURE__ */e.jsx(j,{id:t.id,title:t.title,selected:!0},t.id))),
4
+ /* @__PURE__ */e.jsx(a.Card,{borderTop:!0})]}),
5
+ /* @__PURE__ */e.jsx(a.Button,{mode:"bleed",onClick:k,justify:"flex-start",value:p?"NONE":"ALL",disabled:!!w,children:/* @__PURE__ */e.jsxs(a.Flex,{gap:3,align:"center",children:[
6
+ /* @__PURE__ */e.jsx(a.Text,{size:2,children:p?/* @__PURE__ */e.jsx(n.TextWithTone,{tone:"primary",children:/* @__PURE__ */e.jsx(s.EyeClosedIcon,{})}):/* @__PURE__ */e.jsx(s.EyeOpenIcon,{})}),
7
+ /* @__PURE__ */e.jsx(a.Box,{flex:1,children:/* @__PURE__ */e.jsx(a.Text,{children:p?"Hide all":"Show all"})})]})}),O?/* @__PURE__ */e.jsx(a.TextInput,{onChange:B,value:w,placeholder:"Filter languages"}):/* @__PURE__ */e.jsx(a.Card,{borderTop:!0}),r.filter((e=>!w||e.title.toLowerCase().includes(w.toLowerCase()))).map((t=>/* @__PURE__ */e.jsx(j,{id:t.id,onToggle:y,selected:g.includes(t.id),title:t.title},t.id)))]})}),N=g.length===I?"Showing all":`Showing ${g.length} / ${I}`;/* @__PURE__ */
8
+ return e.jsx(a.Popover,{animate:!0,content:q,open:o,portal:!0,ref:b,children:/* @__PURE__ */e.jsx(a.Button,{text:N,icon:s.TranslateIcon,mode:"bleed",onClick:v,ref:C,selected:o})})}function j(l){const{id:i,onToggle:r,selected:o,title:u}=l,c=t.useCallback((()=>{r&&r(i)}),[i,r]),d=!r;/* @__PURE__ */
9
+ return e.jsx(a.Button,{mode:"bleed",onClick:c,justify:"flex-start",disabled:d,children:/* @__PURE__ */e.jsxs(a.Flex,{gap:3,align:"center",children:[
10
+ /* @__PURE__ */e.jsx(a.Text,{size:2,children:o?/* @__PURE__ */e.jsx(n.TextWithTone,{tone:d?"default":"positive",children:/* @__PURE__ */e.jsx(s.CheckmarkCircleIcon,{})}):/* @__PURE__ */e.jsx(s.CircleIcon,{})}),
11
+ /* @__PURE__ */e.jsx(a.Box,{flex:1,children:/* @__PURE__ */e.jsx(a.Text,{children:u})}),
12
+ /* @__PURE__ */e.jsx(a.Badge,{children:i})]})})}function L(e){const{members:n,schemaType:s,renderDefault:a,...l}=e,{selectedLanguageIds:i,options:r}=f(),{filterField:o}=r,u=e.value;return a({...l,members:t.useMemo((()=>n.filter((e=>"field"===e.kind&&o(s,e,i,u)||"fieldSet"===e.kind||"error"===e.kind)).map((e=>"fieldSet"===e.kind?{...e,fieldSet:{...e.fieldSet,members:e.fieldSet.members.filter((e=>"field"===e.kind&&o(s,e,i,u)))}}:e))),[s,n,o,i,u]),schemaType:s,renderDefault:a})}const y=n.definePlugin((s=>{const a=()=>/* @__PURE__ */e.jsx(h,{}),l={...g.options,...s};return{name:"@sanity/language-filter",studio:{components:{layout:s=>function(s){const a=n.useClient({apiVersion:"2023-01-01"}),[l,i]=t.useState(Array.isArray(s.options.supportedLanguages)?s.options.supportedLanguages:[]);t.useEffect((()=>{let e=[];Array.isArray(s.options.supportedLanguages)||async function(t){e=await t(a,{}),i(e)}(s.options.supportedLanguages)}),[a,s.options.supportedLanguages]);const r=t.useMemo((()=>({...g.options,...s.options,supportedLanguages:l})),[s.options,l]),[o,u]=function(e){return t.useState((()=>[...e.defaultLanguages??[],...c(e)]))}(r);/* @__PURE__ */
13
+ return e.jsx(p.Provider,{value:{options:r,selectedLanguageIds:o,setSelectedLanguageIds:u},children:s.renderDefault(s)})}({...s,options:l})}},document:{unstable_languageFilter:(e,{schemaType:t,schema:n})=>r(n.get(t),s)?[...e,a]:e},form:{components:{input:t=>"root"!==t.id&&n.isObjectSchemaType(t.schemaType)?function(t){const{options:s}=f(),a=n.useFormValue(["_type"]);return r(n.useSchema().get(a),s)?/* @__PURE__ */e.jsx(L,{...t}):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));exports.defaultFilterField=i,exports.isLanguageFilterEnabled=r,exports.languageFilter=y,exports.useLanguageFilterStudioContext=f;//# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/filterField.ts","../src/useSelectedLanguageIds.ts","../src/LanguageFilterStudioContext.tsx","../src/usePaneLanguages.ts","../src/LanguageFilterMenuButton.tsx","../src/LanguageFilterObjectInput.tsx","../src/plugin.tsx"],"sourcesContent":["import type {SchemaType} from 'sanity'\n\nimport type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'\n\nexport const defaultFilterField: FilterFieldFunction = (\n enclosingType,\n field,\n selectedLanguageIds,\n) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)\n\nexport function isLanguageFilterEnabled(\n schemaType: SchemaType | undefined,\n options: LanguageFilterConfig,\n): boolean {\n const schemaFilter =\n isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter\n const defaultEnabled = !options.documentTypes\n\n return !!(\n (defaultEnabled && schemaFilter !== false) ||\n (!defaultEnabled && schemaFilter) ||\n (schemaType && options.documentTypes?.includes(schemaType.name))\n )\n}\n\nfunction isDocument(schemaType?: SchemaType) {\n return schemaType?.jsonType === 'object' && getRootType(schemaType).name === 'document'\n}\n\nfunction getRootType(schema: SchemaType): SchemaType {\n if (schema.type) {\n return getRootType(schema.type)\n }\n return schema\n}\n","import {useState} from 'react'\n\nimport type {Language, LanguageFilterConfig} from './types'\nconst storageKey = '@sanity/plugin/language-filter/selected-languages'\n\nexport function getPersistedLanguageIds(options: LanguageFilterConfig): string[] {\n const selectableLangs = getSelectableLanguages(options).map((l) => l.id)\n\n let selected: string[] = selectableLangs\n try {\n const persistedValue = window.localStorage.getItem(storageKey)\n if (persistedValue) {\n selected = JSON.parse(persistedValue)\n }\n } catch (err) {} // eslint-disable-line no-empty\n\n // constrain persisted/selected languages to the ones currently supported\n selected = intersection(selected, selectableLangs)\n return selected\n}\n\nexport function persistLanguageIds(languageIds: string[]): void {\n window.localStorage.setItem(storageKey, JSON.stringify(languageIds))\n}\n\nfunction intersection(array1: string[], array2: string[]) {\n return array1.filter((value) => array2.includes(value))\n}\n\nexport function getSelectableLanguages({\n supportedLanguages,\n defaultLanguages,\n}: LanguageFilterConfig): Language[] {\n return Array.isArray(supportedLanguages)\n ? supportedLanguages.filter((lang) => !defaultLanguages?.includes(lang.id))\n : []\n}\n\nexport function useSelectedLanguageIds(\n options: LanguageFilterConfig,\n): [string[], (ids: string[]) => void] {\n return useState(() => [...(options.defaultLanguages ?? []), ...getPersistedLanguageIds(options)])\n}\n","import {createContext, useContext, useEffect, useMemo, useState} from 'react'\nimport {type LayoutProps, useClient} from 'sanity'\n\nimport {defaultFilterField} from './filterField'\nimport type {\n Language,\n LanguageCallback,\n LanguageFilterConfig,\n LanguageFilterConfigProcessed,\n} from './types'\nimport {useSelectedLanguageIds} from './useSelectedLanguageIds'\n\nexport interface LanguageFilterStudioContextProps {\n // eslint-disable-next-line react/require-default-props\n options: Required<LanguageFilterConfig>\n}\n\nexport interface LanguageFilterStudioContextProcessed {\n options: Required<LanguageFilterConfigProcessed>\n}\n\nexport interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {\n selectedLanguageIds: string[]\n setSelectedLanguageIds: (ids: string[]) => void\n}\n\nexport const defaultContextValue: LanguageFilterStudioContextValue = {\n options: {\n apiVersion: '2022-11-27',\n supportedLanguages: [],\n defaultLanguages: [],\n documentTypes: [],\n filterField: defaultFilterField,\n },\n selectedLanguageIds: [],\n setSelectedLanguageIds: () => console.error('LanguageFilterStudioContext not initialized'),\n}\n\nconst LanguageFilterStudioContext =\n createContext<LanguageFilterStudioContextValue>(defaultContextValue)\n\n/**\n * This is a separate Provider from the Context that wraps the document pane\n * but it used to listen to changes to the selected language IDs inside it\n * and provide them to a Studio-wide context\n */\nexport function LanguageFilterStudioProvider(\n props: LayoutProps & LanguageFilterStudioContextProps,\n) {\n const client = useClient({apiVersion: '2023-01-01'})\n const [languages, setLanguages] = useState<Language[]>(\n Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],\n )\n useEffect(() => {\n let asyncLanguages: Language[] = []\n\n async function getLanguages(supportedLanguagesCallback: LanguageCallback) {\n asyncLanguages = await supportedLanguagesCallback(client, {})\n setLanguages(asyncLanguages)\n }\n\n if (!Array.isArray(props.options.supportedLanguages)) {\n getLanguages(props.options.supportedLanguages)\n }\n }, [client, props.options.supportedLanguages])\n\n const options = useMemo<Required<LanguageFilterConfigProcessed>>(() => {\n return {\n ...defaultContextValue.options,\n ...props.options,\n supportedLanguages: languages,\n }\n }, [props.options, languages])\n\n const [selectedLanguageIds, setSelectedLanguageIds] = useSelectedLanguageIds(options)\n\n return (\n <LanguageFilterStudioContext.Provider\n value={{options, selectedLanguageIds, setSelectedLanguageIds}}\n >\n {props.renderDefault(props)}\n </LanguageFilterStudioContext.Provider>\n )\n}\n\n/**\n * Retrieves plugin options and the currently selected\n * language IDs from anywhere in the Studio\n */\nexport function useLanguageFilterStudioContext() {\n return useContext(LanguageFilterStudioContext)\n}\n","import {useCallback, useMemo} from 'react'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'\n\nconst unique = (arr: string[]) => Array.from(new Set(arr))\n\nexport function usePaneLanguages(): {\n activeLanguages: string[]\n allSelected: boolean\n selectAll: () => void\n selectNone: () => void\n toggleLanguage: (languageId: string) => void\n} {\n const {selectedLanguageIds, setSelectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {defaultLanguages = []} = options\n\n const selectableLanguages = useMemo(() => getSelectableLanguages(options), [options])\n\n const updateSelectedIds = useCallback(\n (ids: string[]) => {\n setSelectedLanguageIds(unique([...defaultLanguages, ...ids]))\n persistLanguageIds(unique([...defaultLanguages, ...ids]))\n },\n [defaultLanguages, setSelectedLanguageIds],\n )\n\n const selectAll = useCallback(\n () => updateSelectedIds(selectableLanguages.map((l) => l.id)),\n [updateSelectedIds, selectableLanguages],\n )\n\n const selectNone = useCallback(() => {\n updateSelectedIds(defaultLanguages)\n }, [defaultLanguages, updateSelectedIds])\n\n const toggleLanguage = useCallback(\n (languageId: string) => {\n let lang = selectedLanguageIds\n\n if (lang.includes(languageId)) {\n lang = lang.filter((l) => l !== languageId)\n } else {\n lang = unique([...lang, languageId])\n }\n\n updateSelectedIds(lang)\n },\n [updateSelectedIds, selectedLanguageIds],\n )\n\n const activeLanguages = useMemo(\n () => unique([...(defaultLanguages ?? []), ...selectedLanguageIds]),\n [defaultLanguages, selectedLanguageIds],\n )\n\n return {\n activeLanguages,\n allSelected:\n selectedLanguageIds.length === selectableLanguages.length + defaultLanguages.length,\n selectAll,\n selectNone,\n toggleLanguage,\n }\n}\n","import {\n CheckmarkCircleIcon,\n CircleIcon,\n EyeClosedIcon,\n EyeOpenIcon,\n TranslateIcon,\n} from '@sanity/icons'\nimport {\n Badge,\n Box,\n Button,\n Card,\n Flex,\n Popover,\n Stack,\n Text,\n TextInput,\n useClickOutside,\n} from '@sanity/ui'\nimport {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'\nimport {TextWithTone} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {usePaneLanguages} from './usePaneLanguages'\n\nconst StyledBox = styled(Box)`\n max-height: calc(100vh - 200px);\n`\n\nexport function LanguageFilterMenuButton() {\n const {options} = useLanguageFilterStudioContext()\n\n const defaultLanguages = options.supportedLanguages.filter((l) =>\n options.defaultLanguages?.includes(l.id),\n )\n\n const languageOptions = options.supportedLanguages.filter(\n (l) => !options.defaultLanguages?.includes(l.id),\n )\n const [open, setOpen] = useState(false)\n const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()\n const [button, setButton] = useState<HTMLElement | null>(null)\n const [popover, setPopover] = useState<HTMLElement | null>(null)\n\n const handleToggleAll: MouseEventHandler<HTMLButtonElement> = useCallback(\n (event) => {\n const checked = event.currentTarget.value === 'ALL'\n\n if (checked) {\n selectAll()\n } else {\n selectNone()\n }\n },\n [selectAll, selectNone],\n )\n\n const handleClick = useCallback(() => setOpen((o) => !o), [])\n\n const handleClickOutside = useCallback(() => setOpen(false), [])\n\n useClickOutside(handleClickOutside, [button, popover])\n\n const langCount = options.supportedLanguages.length\n\n // Search filter query\n const [query, setQuery] = useState('')\n const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {\n if (event.currentTarget.value) {\n setQuery(event.currentTarget.value)\n } else {\n setQuery('')\n }\n }, [])\n\n const showSearch = langCount > 4\n\n const content = (\n <StyledBox overflow=\"auto\">\n <Stack padding={1} space={1}>\n {defaultLanguages.length > 0 && (\n <>\n {defaultLanguages.map((l) => (\n <LanguageFilterOption key={l.id} id={l.id} title={l.title} selected />\n ))}\n <Card borderTop />\n </>\n )}\n\n <Button\n mode=\"bleed\"\n onClick={handleToggleAll}\n justify=\"flex-start\"\n value={allSelected ? 'NONE' : 'ALL'}\n disabled={!!query}\n >\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {allSelected ? (\n <TextWithTone tone=\"primary\">\n <EyeClosedIcon />\n </TextWithTone>\n ) : (\n <EyeOpenIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{allSelected ? 'Hide all' : 'Show all'}</Text>\n </Box>\n </Flex>\n </Button>\n\n {showSearch ? (\n <TextInput onChange={handleQuery} value={query} placeholder=\"Filter languages\" />\n ) : (\n <Card borderTop />\n )}\n\n {languageOptions\n .filter((language) => {\n if (query) {\n return language.title.toLowerCase().includes(query.toLowerCase())\n }\n return true\n })\n .map((lang) => (\n <LanguageFilterOption\n id={lang.id}\n key={lang.id}\n onToggle={toggleLanguage}\n selected={activeLanguages.includes(lang.id)}\n title={lang.title}\n />\n ))}\n </Stack>\n </StyledBox>\n )\n\n const buttonText =\n activeLanguages.length === langCount\n ? 'Showing all'\n : `Showing ${activeLanguages.length} / ${langCount}`\n return (\n <Popover animate content={content} open={open} portal ref={setPopover}>\n <Button\n text={buttonText}\n icon={TranslateIcon}\n mode=\"bleed\"\n onClick={handleClick}\n ref={setButton}\n selected={open}\n />\n </Popover>\n )\n}\n\nfunction LanguageFilterOption(props: {\n id: string\n selected: boolean\n title: string\n // eslint-disable-next-line react/require-default-props\n onToggle?: (id: string) => void\n}) {\n const {id, onToggle, selected, title} = props\n\n const handleChange = useCallback(() => {\n if (onToggle) {\n onToggle(id)\n }\n }, [id, onToggle])\n\n const disabled = !onToggle\n\n return (\n <Button mode=\"bleed\" onClick={handleChange} justify=\"flex-start\" disabled={disabled}>\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {selected ? (\n <TextWithTone tone={disabled ? 'default' : 'positive'}>\n <CheckmarkCircleIcon />\n </TextWithTone>\n ) : (\n <CircleIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{title}</Text>\n </Box>\n <Badge>{id}</Badge>\n </Flex>\n </Button>\n )\n}\n","import {useMemo} from 'react'\nimport {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\n\n// First check that this Object is in a schema type for which language-filter is enabled\nexport function FilteredObjectWrapper(props: ObjectInputProps) {\n const {options} = useLanguageFilterStudioContext()\n\n const documentType = useFormValue(['_type']) as string\n const schema = useSchema()\n const languageFilterEnabled = isLanguageFilterEnabled(schema.get(documentType), options)\n return languageFilterEnabled ? <FilteredObjectInput {...props} /> : props.renderDefault(props)\n}\n\n// Modify the object members based on selected languages in the filter\nexport function FilteredObjectInput(props: ObjectInputProps) {\n const {members: membersProp, schemaType, renderDefault, ...restProps} = props\n const {selectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {filterField} = options\n\n const members: ObjectMember[] = useMemo(() => {\n return membersProp\n .filter((member) => {\n return (\n (member.kind === 'field' && filterField(schemaType, member, selectedLanguageIds)) ||\n member.kind === 'fieldSet' ||\n member.kind === 'error'\n )\n })\n .map((member) => {\n if (member.kind === 'fieldSet') {\n return {\n ...member,\n fieldSet: {\n ...member.fieldSet,\n members: member.fieldSet.members.filter((fieldsetMember) => {\n return (\n fieldsetMember.kind === 'field' &&\n filterField(schemaType, fieldsetMember, selectedLanguageIds)\n )\n }),\n },\n }\n }\n return member\n })\n }, [schemaType, membersProp, filterField, selectedLanguageIds])\n\n return renderDefault({...restProps, members, schemaType, renderDefault})\n}\n","import {\n definePlugin,\n type DocumentLanguageFilterComponent,\n isObjectSchemaType,\n type ObjectInputProps,\n} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {LanguageFilterMenuButton} from './LanguageFilterMenuButton'\nimport {FilteredObjectWrapper} from './LanguageFilterObjectInput'\nimport {defaultContextValue, LanguageFilterStudioProvider} from './LanguageFilterStudioContext'\nimport type {LanguageFilterConfig} from './types'\n\n/**\n * ## Usage in sanity.config.ts (or .js)\n *\n * ```\n * import {defineConfig} from 'sanity'\n * import {languageFilter} from '@sanity/language-filter'\n *\n * export const defineConfig({\n * /...\n * plugins: [\n * languageFilter({\n * supportedLanguages: [\n * {id: 'nb', title: 'Norwegian (Bokmål)'},\n * {id: 'nn', title: 'Norwegian (Nynorsk)'},\n * {id: 'en', title: 'English'},\n * {id: 'es', title: 'Spanish'},\n * {id: 'arb', title: 'Arabic'},\n * {id: 'pt', title: 'Portuguese'},\n * //...\n * ],\n * // Select Norwegian (Bokmål) by default\n * defaultLanguages: ['nb'],\n * // Only show language filter for document type `page` (schemaType.name)\n * // Can also enable via document-options: options.languageFilter: true\n * documentTypes: ['page'],\n * // default filter function shown\n * filterField: (enclosingType, field, selectedLanguageIds) =>\n * !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name),\n * })\n * ]\n * })\n * ```\n */\nexport const languageFilter = definePlugin<LanguageFilterConfig>((options) => {\n const RenderLanguageFilter: DocumentLanguageFilterComponent = () => {\n return <LanguageFilterMenuButton />\n }\n\n const pluginOptions = {\n ...defaultContextValue.options,\n ...options,\n }\n\n return {\n name: '@sanity/language-filter',\n studio: {\n components: {\n layout: (props) => LanguageFilterStudioProvider({...props, options: pluginOptions}),\n },\n },\n\n document: {\n unstable_languageFilter: (prev, {schemaType, schema}) => {\n if (isLanguageFilterEnabled(schema.get(schemaType), options)) {\n return [...prev, RenderLanguageFilter]\n }\n return prev\n },\n },\n\n form: {\n components: {\n input: (props) => {\n if (props.id !== 'root' && isObjectSchemaType(props.schemaType)) {\n return FilteredObjectWrapper(props as ObjectInputProps)\n }\n\n return props.renderDefault(props)\n },\n },\n },\n }\n})\n"],"names":["defaultFilterField","enclosingType","field","selectedLanguageIds","name","startsWith","includes","isLanguageFilterEnabled","schemaType","options","_a","_b","schemaFilter","jsonType","getRootType","isDocument","languageFilter","defaultEnabled","documentTypes","schema","type","storageKey","getPersistedLanguageIds","selectableLangs","getSelectableLanguages","map","l","id","selected","persistedValue","window","localStorage","getItem","JSON","parse","err","array2","filter","value","supportedLanguages","defaultLanguages","Array","isArray","lang","defaultContextValue","apiVersion","filterField","setSelectedLanguageIds","console","error","LanguageFilterStudioContext","createContext","LanguageFilterStudioProvider","props","client","useClient","languages","setLanguages","useState","useEffect","asyncLanguages","async","supportedLanguagesCallback","getLanguages","useMemo","__spreadProps","useSelectedLanguageIds","jsxRuntime","jsx","Provider","children","renderDefault","useLanguageFilterStudioContext","useContext","unique","arr","from","Set","usePaneLanguages","selectableLanguages","updateSelectedIds","useCallback","ids","languageIds","setItem","stringify","selectAll","selectNone","toggleLanguage","languageId","activeLanguages","allSelected","length","StyledBox","styled","Box","LanguageFilterMenuButton","languageOptions","open","setOpen","button","setButton","popover","setPopover","handleToggleAll","event","currentTarget","handleClick","o","handleClickOutside","useClickOutside","langCount","query","setQuery","handleQuery","showSearch","content","overflow","jsxs","Stack","padding","space","Fragment","LanguageFilterOption","title","Card","borderTop","Button","mode","onClick","justify","disabled","Flex","gap","align","Text","size","TextWithTone","tone","EyeClosedIcon","EyeOpenIcon","flex","TextInput","onChange","placeholder","language","toLowerCase","onToggle","buttonText","Popover","animate","portal","ref","text","icon","TranslateIcon","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","restProps","__objRest","member","kind","__spreadValues","fieldSet","fieldsetMember","definePlugin","RenderLanguageFilter","pluginOptions","studio","components","layout","document","unstable_languageFilter","prev","get","form","input","isObjectSchemaType","documentType","useFormValue","useSchema","FilteredObjectWrapper","exports"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/filterField.ts","../src/useSelectedLanguageIds.ts","../src/LanguageFilterStudioContext.tsx","../src/usePaneLanguages.ts","../src/LanguageFilterMenuButton.tsx","../src/LanguageFilterObjectInput.tsx","../src/plugin.tsx"],"sourcesContent":["import type {SchemaType} from 'sanity'\n\nimport type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'\n\nexport const defaultFilterField: FilterFieldFunction = (\n enclosingType,\n field,\n selectedLanguageIds,\n) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)\n\nexport function isLanguageFilterEnabled(\n schemaType: SchemaType | undefined,\n options: LanguageFilterConfig,\n): boolean {\n const schemaFilter =\n isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter\n const defaultEnabled = !options.documentTypes\n\n return !!(\n (defaultEnabled && schemaFilter !== false) ||\n (!defaultEnabled && schemaFilter) ||\n (schemaType && options.documentTypes?.includes(schemaType.name))\n )\n}\n\nfunction isDocument(schemaType?: SchemaType) {\n return schemaType?.jsonType === 'object' && getRootType(schemaType).name === 'document'\n}\n\nfunction getRootType(schema: SchemaType): SchemaType {\n if (schema.type) {\n return getRootType(schema.type)\n }\n return schema\n}\n","import {useState} from 'react'\n\nimport type {Language, LanguageFilterConfig} from './types'\nconst storageKey = '@sanity/plugin/language-filter/selected-languages'\n\nexport function getPersistedLanguageIds(options: LanguageFilterConfig): string[] {\n const selectableLangs = getSelectableLanguages(options).map((l) => l.id)\n\n let selected: string[] = selectableLangs\n try {\n const persistedValue = window.localStorage.getItem(storageKey)\n if (persistedValue) {\n selected = JSON.parse(persistedValue)\n }\n } catch (err) {} // eslint-disable-line no-empty\n\n // constrain persisted/selected languages to the ones currently supported\n selected = intersection(selected, selectableLangs)\n return selected\n}\n\nexport function persistLanguageIds(languageIds: string[]): void {\n window.localStorage.setItem(storageKey, JSON.stringify(languageIds))\n}\n\nfunction intersection(array1: string[], array2: string[]) {\n return array1.filter((value) => array2.includes(value))\n}\n\nexport function getSelectableLanguages({\n supportedLanguages,\n defaultLanguages,\n}: LanguageFilterConfig): Language[] {\n return Array.isArray(supportedLanguages)\n ? supportedLanguages.filter((lang) => !defaultLanguages?.includes(lang.id))\n : []\n}\n\nexport function useSelectedLanguageIds(\n options: LanguageFilterConfig,\n): [string[], (ids: string[]) => void] {\n return useState(() => [...(options.defaultLanguages ?? []), ...getPersistedLanguageIds(options)])\n}\n","import {createContext, useContext, useEffect, useMemo, useState} from 'react'\nimport {type LayoutProps, useClient} from 'sanity'\n\nimport {defaultFilterField} from './filterField'\nimport type {\n Language,\n LanguageCallback,\n LanguageFilterConfig,\n LanguageFilterConfigProcessed,\n} from './types'\nimport {useSelectedLanguageIds} from './useSelectedLanguageIds'\n\nexport interface LanguageFilterStudioContextProps {\n // eslint-disable-next-line react/require-default-props\n options: Required<LanguageFilterConfig>\n}\n\nexport interface LanguageFilterStudioContextProcessed {\n options: Required<LanguageFilterConfigProcessed>\n}\n\nexport interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {\n selectedLanguageIds: string[]\n setSelectedLanguageIds: (ids: string[]) => void\n}\n\nexport const defaultContextValue: LanguageFilterStudioContextValue = {\n options: {\n apiVersion: '2022-11-27',\n supportedLanguages: [],\n defaultLanguages: [],\n documentTypes: [],\n filterField: defaultFilterField,\n },\n selectedLanguageIds: [],\n setSelectedLanguageIds: () => console.error('LanguageFilterStudioContext not initialized'),\n}\n\nconst LanguageFilterStudioContext =\n createContext<LanguageFilterStudioContextValue>(defaultContextValue)\n\n/**\n * This is a separate Provider from the Context that wraps the document pane\n * but it used to listen to changes to the selected language IDs inside it\n * and provide them to a Studio-wide context\n */\nexport function LanguageFilterStudioProvider(\n props: LayoutProps & LanguageFilterStudioContextProps,\n) {\n const client = useClient({apiVersion: '2023-01-01'})\n const [languages, setLanguages] = useState<Language[]>(\n Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],\n )\n useEffect(() => {\n let asyncLanguages: Language[] = []\n\n async function getLanguages(supportedLanguagesCallback: LanguageCallback) {\n asyncLanguages = await supportedLanguagesCallback(client, {})\n setLanguages(asyncLanguages)\n }\n\n if (!Array.isArray(props.options.supportedLanguages)) {\n getLanguages(props.options.supportedLanguages)\n }\n }, [client, props.options.supportedLanguages])\n\n const options = useMemo<Required<LanguageFilterConfigProcessed>>(() => {\n return {\n ...defaultContextValue.options,\n ...props.options,\n supportedLanguages: languages,\n }\n }, [props.options, languages])\n\n const [selectedLanguageIds, setSelectedLanguageIds] = useSelectedLanguageIds(options)\n\n return (\n <LanguageFilterStudioContext.Provider\n value={{options, selectedLanguageIds, setSelectedLanguageIds}}\n >\n {props.renderDefault(props)}\n </LanguageFilterStudioContext.Provider>\n )\n}\n\n/**\n * Retrieves plugin options and the currently selected\n * language IDs from anywhere in the Studio\n */\nexport function useLanguageFilterStudioContext() {\n return useContext(LanguageFilterStudioContext)\n}\n","import {useCallback, useMemo} from 'react'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'\n\nconst unique = (arr: string[]) => Array.from(new Set(arr))\n\nexport function usePaneLanguages(): {\n activeLanguages: string[]\n allSelected: boolean\n selectAll: () => void\n selectNone: () => void\n toggleLanguage: (languageId: string) => void\n} {\n const {selectedLanguageIds, setSelectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {defaultLanguages = []} = options\n\n const selectableLanguages = useMemo(() => getSelectableLanguages(options), [options])\n\n const updateSelectedIds = useCallback(\n (ids: string[]) => {\n setSelectedLanguageIds(unique([...defaultLanguages, ...ids]))\n persistLanguageIds(unique([...defaultLanguages, ...ids]))\n },\n [defaultLanguages, setSelectedLanguageIds],\n )\n\n const selectAll = useCallback(\n () => updateSelectedIds(selectableLanguages.map((l) => l.id)),\n [updateSelectedIds, selectableLanguages],\n )\n\n const selectNone = useCallback(() => {\n updateSelectedIds(defaultLanguages)\n }, [defaultLanguages, updateSelectedIds])\n\n const toggleLanguage = useCallback(\n (languageId: string) => {\n let lang = selectedLanguageIds\n\n if (lang.includes(languageId)) {\n lang = lang.filter((l) => l !== languageId)\n } else {\n lang = unique([...lang, languageId])\n }\n\n updateSelectedIds(lang)\n },\n [updateSelectedIds, selectedLanguageIds],\n )\n\n const activeLanguages = useMemo(\n () => unique([...(defaultLanguages ?? []), ...selectedLanguageIds]),\n [defaultLanguages, selectedLanguageIds],\n )\n\n return {\n activeLanguages,\n allSelected:\n selectedLanguageIds.length === selectableLanguages.length + defaultLanguages.length,\n selectAll,\n selectNone,\n toggleLanguage,\n }\n}\n","import {\n CheckmarkCircleIcon,\n CircleIcon,\n EyeClosedIcon,\n EyeOpenIcon,\n TranslateIcon,\n} from '@sanity/icons'\nimport {\n Badge,\n Box,\n Button,\n Card,\n Flex,\n Popover,\n Stack,\n Text,\n TextInput,\n useClickOutside,\n} from '@sanity/ui'\nimport {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'\nimport {TextWithTone} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {usePaneLanguages} from './usePaneLanguages'\n\nconst StyledBox = styled(Box)`\n max-height: calc(100vh - 200px);\n`\n\nexport function LanguageFilterMenuButton() {\n const {options} = useLanguageFilterStudioContext()\n\n const defaultLanguages = options.supportedLanguages.filter((l) =>\n options.defaultLanguages?.includes(l.id),\n )\n\n const languageOptions = options.supportedLanguages.filter(\n (l) => !options.defaultLanguages?.includes(l.id),\n )\n const [open, setOpen] = useState(false)\n const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()\n const [button, setButton] = useState<HTMLElement | null>(null)\n const [popover, setPopover] = useState<HTMLElement | null>(null)\n\n const handleToggleAll: MouseEventHandler<HTMLButtonElement> = useCallback(\n (event) => {\n const checked = event.currentTarget.value === 'ALL'\n\n if (checked) {\n selectAll()\n } else {\n selectNone()\n }\n },\n [selectAll, selectNone],\n )\n\n const handleClick = useCallback(() => setOpen((o) => !o), [])\n\n const handleClickOutside = useCallback(() => setOpen(false), [])\n\n useClickOutside(handleClickOutside, [button, popover])\n\n const langCount = options.supportedLanguages.length\n\n // Search filter query\n const [query, setQuery] = useState('')\n const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {\n if (event.currentTarget.value) {\n setQuery(event.currentTarget.value)\n } else {\n setQuery('')\n }\n }, [])\n\n const showSearch = langCount > 4\n\n const content = (\n <StyledBox overflow=\"auto\">\n <Stack padding={1} space={1}>\n {defaultLanguages.length > 0 && (\n <>\n {defaultLanguages.map((l) => (\n <LanguageFilterOption key={l.id} id={l.id} title={l.title} selected />\n ))}\n <Card borderTop />\n </>\n )}\n\n <Button\n mode=\"bleed\"\n onClick={handleToggleAll}\n justify=\"flex-start\"\n value={allSelected ? 'NONE' : 'ALL'}\n disabled={!!query}\n >\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {allSelected ? (\n <TextWithTone tone=\"primary\">\n <EyeClosedIcon />\n </TextWithTone>\n ) : (\n <EyeOpenIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{allSelected ? 'Hide all' : 'Show all'}</Text>\n </Box>\n </Flex>\n </Button>\n\n {showSearch ? (\n <TextInput onChange={handleQuery} value={query} placeholder=\"Filter languages\" />\n ) : (\n <Card borderTop />\n )}\n\n {languageOptions\n .filter((language) => {\n if (query) {\n return language.title.toLowerCase().includes(query.toLowerCase())\n }\n return true\n })\n .map((lang) => (\n <LanguageFilterOption\n id={lang.id}\n key={lang.id}\n onToggle={toggleLanguage}\n selected={activeLanguages.includes(lang.id)}\n title={lang.title}\n />\n ))}\n </Stack>\n </StyledBox>\n )\n\n const buttonText =\n activeLanguages.length === langCount\n ? 'Showing all'\n : `Showing ${activeLanguages.length} / ${langCount}`\n return (\n <Popover animate content={content} open={open} portal ref={setPopover}>\n <Button\n text={buttonText}\n icon={TranslateIcon}\n mode=\"bleed\"\n onClick={handleClick}\n ref={setButton}\n selected={open}\n />\n </Popover>\n )\n}\n\nfunction LanguageFilterOption(props: {\n id: string\n selected: boolean\n title: string\n // eslint-disable-next-line react/require-default-props\n onToggle?: (id: string) => void\n}) {\n const {id, onToggle, selected, title} = props\n\n const handleChange = useCallback(() => {\n if (onToggle) {\n onToggle(id)\n }\n }, [id, onToggle])\n\n const disabled = !onToggle\n\n return (\n <Button mode=\"bleed\" onClick={handleChange} justify=\"flex-start\" disabled={disabled}>\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {selected ? (\n <TextWithTone tone={disabled ? 'default' : 'positive'}>\n <CheckmarkCircleIcon />\n </TextWithTone>\n ) : (\n <CircleIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{title}</Text>\n </Box>\n <Badge>{id}</Badge>\n </Flex>\n </Button>\n )\n}\n","import {useMemo} from 'react'\nimport {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\n\n// First check that this Object is in a schema type for which language-filter is enabled\nexport function FilteredObjectWrapper(props: ObjectInputProps) {\n const {options} = useLanguageFilterStudioContext()\n\n const documentType = useFormValue(['_type']) as string\n const schema = useSchema()\n const languageFilterEnabled = isLanguageFilterEnabled(schema.get(documentType), options)\n return languageFilterEnabled ? <FilteredObjectInput {...props} /> : props.renderDefault(props)\n}\n\n// Modify the object members based on selected languages in the filter\nexport function FilteredObjectInput(props: ObjectInputProps) {\n const {members: membersProp, schemaType, renderDefault, ...restProps} = props\n const {selectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {filterField} = options\n const parentValue = props.value\n\n const members: ObjectMember[] = useMemo(() => {\n return membersProp\n .filter((member) => {\n return (\n (member.kind === 'field' &&\n filterField(schemaType, member, selectedLanguageIds, parentValue)) ||\n member.kind === 'fieldSet' ||\n member.kind === 'error'\n )\n })\n .map((member) => {\n if (member.kind === 'fieldSet') {\n return {\n ...member,\n fieldSet: {\n ...member.fieldSet,\n members: member.fieldSet.members.filter((fieldsetMember) => {\n return (\n fieldsetMember.kind === 'field' &&\n filterField(schemaType, fieldsetMember, selectedLanguageIds, parentValue)\n )\n }),\n },\n }\n }\n return member\n })\n }, [schemaType, membersProp, filterField, selectedLanguageIds, parentValue])\n\n return renderDefault({...restProps, members, schemaType, renderDefault})\n}\n","import {\n definePlugin,\n type DocumentLanguageFilterComponent,\n isObjectSchemaType,\n type ObjectInputProps,\n} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {LanguageFilterMenuButton} from './LanguageFilterMenuButton'\nimport {FilteredObjectWrapper} from './LanguageFilterObjectInput'\nimport {defaultContextValue, LanguageFilterStudioProvider} from './LanguageFilterStudioContext'\nimport type {LanguageFilterConfig} from './types'\n\n/**\n * ## Usage in sanity.config.ts (or .js)\n *\n * ```\n * import {defineConfig} from 'sanity'\n * import {languageFilter} from '@sanity/language-filter'\n *\n * export const defineConfig({\n * /...\n * plugins: [\n * languageFilter({\n * supportedLanguages: [\n * {id: 'nb', title: 'Norwegian (Bokmål)'},\n * {id: 'nn', title: 'Norwegian (Nynorsk)'},\n * {id: 'en', title: 'English'},\n * {id: 'es', title: 'Spanish'},\n * {id: 'arb', title: 'Arabic'},\n * {id: 'pt', title: 'Portuguese'},\n * //...\n * ],\n * // Select Norwegian (Bokmål) by default\n * defaultLanguages: ['nb'],\n * // Only show language filter for document type `page` (schemaType.name)\n * // Can also enable via document-options: options.languageFilter: true\n * documentTypes: ['page'],\n * // default filter function shown\n * filterField: (enclosingType, field, selectedLanguageIds) =>\n * !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name),\n * })\n * ]\n * })\n * ```\n */\nexport const languageFilter = definePlugin<LanguageFilterConfig>((options) => {\n const RenderLanguageFilter: DocumentLanguageFilterComponent = () => {\n return <LanguageFilterMenuButton />\n }\n\n const pluginOptions = {\n ...defaultContextValue.options,\n ...options,\n }\n\n return {\n name: '@sanity/language-filter',\n studio: {\n components: {\n layout: (props) => LanguageFilterStudioProvider({...props, options: pluginOptions}),\n },\n },\n\n document: {\n unstable_languageFilter: (prev, {schemaType, schema}) => {\n if (isLanguageFilterEnabled(schema.get(schemaType), options)) {\n return [...prev, RenderLanguageFilter]\n }\n return prev\n },\n },\n\n form: {\n components: {\n input: (props) => {\n if (props.id !== 'root' && isObjectSchemaType(props.schemaType)) {\n return FilteredObjectWrapper(props as ObjectInputProps)\n }\n\n return props.renderDefault(props)\n },\n },\n },\n }\n})\n"],"names":["defaultFilterField","enclosingType","field","selectedLanguageIds","name","startsWith","includes","isLanguageFilterEnabled","schemaType","options","schemaFilter","jsonType","getRootType","isDocument","languageFilter","defaultEnabled","documentTypes","schema","type","storageKey","getPersistedLanguageIds","selectableLangs","getSelectableLanguages","map","l","id","selected","persistedValue","window","localStorage","getItem","JSON","parse","array2","filter","value","supportedLanguages","defaultLanguages","Array","isArray","lang","defaultContextValue","apiVersion","filterField","setSelectedLanguageIds","console","error","LanguageFilterStudioContext","createContext","useLanguageFilterStudioContext","useContext","unique","arr","from","Set","StyledBox","styled","Box","LanguageFilterMenuButton","languageOptions","open","setOpen","useState","activeLanguages","allSelected","selectAll","selectNone","toggleLanguage","selectableLanguages","useMemo","updateSelectedIds","useCallback","ids","languageIds","setItem","stringify","languageId","length","usePaneLanguages","button","setButton","popover","setPopover","handleToggleAll","event","currentTarget","handleClick","o","handleClickOutside","useClickOutside","langCount","query","setQuery","handleQuery","showSearch","content","jsx","overflow","children","jsxs","Stack","padding","space","Fragment","LanguageFilterOption","title","Card","borderTop","Button","mode","onClick","justify","disabled","Flex","gap","align","Text","size","TextWithTone","tone","EyeClosedIcon","EyeOpenIcon","flex","TextInput","onChange","placeholder","language","toLowerCase","onToggle","buttonText","Popover","animate","portal","ref","text","icon","TranslateIcon","props","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","renderDefault","restProps","parentValue","member","kind","fieldSet","fieldsetMember","definePlugin","RenderLanguageFilter","pluginOptions","studio","components","layout","client","useClient","languages","setLanguages","useEffect","asyncLanguages","async","supportedLanguagesCallback","getLanguages","useSelectedLanguageIds","Provider","LanguageFilterStudioProvider","document","unstable_languageFilter","prev","get","form","input","isObjectSchemaType","documentType","useFormValue","useSchema","FilteredObjectWrapper","exports"],"mappings":"gOAIO,MAAMA,EAA0C,CACrDC,EACAC,EACAC,KACIF,EAAcG,KAAKC,WAAW,WAAaF,EAAoBG,SAASJ,EAAME,MAE7E,SAASG,EACdC,EACAC,GAEA,MAAMC,EAWR,SAAoBF,GAClB,MAAgC,WAAzBA,GAAYG,UAA0D,aAAjCC,EAAYJ,GAAYJ,IACtE,CAZIS,CAAWL,IAAgBA,GAAqCC,SAASK,eACrEC,GAAkBN,EAAQO,cAEhC,SACGD,IAAmC,IAAjBL,IACjBK,GAAkBL,GACnBF,GAAcC,EAAQO,eAAeV,SAASE,EAAWJ,MAE9D,CAMA,SAASQ,EAAYK,GACnB,OAAIA,EAAOC,KACFN,EAAYK,EAAOC,MAErBD,CACT,CC/BA,MAAME,EAAa,oDAEZ,SAASC,EAAwBX,GACtC,MAAMY,EAAkBC,EAAuBb,GAASc,KAAKC,GAAMA,EAAEC,KAErE,IAAIC,EAAqBL,EACzB,IACE,MAAMM,EAAiBC,OAAOC,aAAaC,QAAQX,GAC/CQ,IACFD,EAAWK,KAAKC,MAAML,GAE1B,CAAA,MAAe,CAGf,OAQsCM,EARJZ,EAAlCK,EAAwBA,EASVQ,QAAQC,GAAUF,EAAO3B,SAAS6B,KARzCT,EAOT,IAAwCO,CANxC,CAUO,SAASX,GAAuBc,mBACrCA,EAAAC,iBACAA,IAEA,OAAOC,MAAMC,QAAQH,GACjBA,EAAmBF,QAAQM,IAAUH,GAAkB/B,SAASkC,EAAKf,MACrE,EACN,CCVO,MAAMgB,EAAwD,CACnEhC,QAAS,CACPiC,WAAY,aACZN,mBAAoB,GACpBC,iBAAkB,GAClBrB,cAAe,GACf2B,YAAa3C,GAEfG,oBAAqB,GACrByC,uBAAwB,IAAMC,QAAQC,MAAM,gDAGxCC,EACJC,EAAAA,cAAgDP,GAkD3C,SAASQ,IACd,OAAOC,EAAAA,WAAWH,EACpB,CCtFA,MAAMI,EAAUC,GAAkBd,MAAMe,KAAK,IAAIC,IAAIF,ICqB/CG,EAAYC,EAAAA,OAAOC,MAAG;;EAIrB,SAASC,IACd,MAAMjD,QAACA,GAAWwC,IAEZZ,EAAmB5B,EAAQ2B,mBAAmBF,QAAQV,GAC1Df,EAAQ4B,kBAAkB/B,SAASkB,EAAEC,MAGjCkC,EAAkBlD,EAAQ2B,mBAAmBF,QAChDV,IAAOf,EAAQ4B,kBAAkB/B,SAASkB,EAAEC,OAExCmC,EAAMC,GAAWC,EAAAA,UAAS,IAC3BC,gBAACA,EAAAC,YAAiBA,EAAAC,UAAaA,EAAAC,WAAWA,EAAAC,eAAYA,GDlCvD,WAOL,MAAMhE,oBAACA,EAAAyC,uBAAqBA,EAAAnC,QAAwBA,GAAWwC,KACzDZ,iBAACA,EAAmB,IAAM5B,EAE1B2D,EAAsBC,EAAAA,SAAQ,IAAM/C,EAAuBb,IAAU,CAACA,IAEtE6D,EAAoBC,EAAAA,aACvBC,IFCE,IAA4BC,EEA7B7B,EAAuBO,EAAO,IAAId,KAAqBmC,KFA1BC,EECVtB,EAAO,IAAId,KAAqBmC,IFAvD5C,OAAOC,aAAa6C,QAAQvD,EAAYY,KAAK4C,UAAUF,GEAK,GAE1D,CAACpC,EAAkBO,IAGfqB,EAAYM,EAAAA,aAChB,IAAMD,EAAkBF,EAAoB7C,KAAKC,GAAMA,EAAEC,OACzD,CAAC6C,EAAmBF,IAGhBF,EAAaK,EAAAA,aAAY,KAC7BD,EAAkBjC,EAAgB,GACjC,CAACA,EAAkBiC,IAEhBH,EAAiBI,EAAAA,aACpBK,IACC,IAAIpC,EAAOrC,EAGTqC,EADEA,EAAKlC,SAASsE,GACTpC,EAAKN,QAAQV,GAAMA,IAAMoD,IAEzBzB,EAAO,IAAIX,EAAMoC,IAG1BN,EAAkB9B,EAAI,GAExB,CAAC8B,EAAmBnE,IAQtB,MAAO,CACL4D,gBANsBM,EAAAA,SACtB,IAAMlB,EAAO,IAAKd,GAAoB,MAAQlC,KAC9C,CAACkC,EAAkBlC,IAKnB6D,YACE7D,EAAoB0E,SAAWT,EAAoBS,OAASxC,EAAiBwC,OAC/EZ,YACAC,aACAC,iBAEJ,CCvBgFW,IACvEC,EAAQC,GAAalB,EAAAA,SAA6B,OAClDmB,EAASC,GAAcpB,EAAAA,SAA6B,MAErDqB,EAAwDZ,EAAAA,aAC3Da,IAC+C,QAA9BA,EAAMC,cAAclD,MAGlC8B,IAEAC,GAAA,GAGJ,CAACD,EAAWC,IAGRoB,EAAcf,EAAAA,aAAY,IAAMV,GAAS0B,IAAOA,KAAI,IAEpDC,EAAqBjB,EAAAA,aAAY,IAAMV,GAAQ,IAAQ,IAE7D4B,EAAAA,gBAAgBD,EAAoB,CAACT,EAAQE,IAE7C,MAAMS,EAAYjF,EAAQ2B,mBAAmByC,QAGtCc,EAAOC,GAAY9B,EAAAA,SAAS,IAC7B+B,EAActB,EAAAA,aAAaa,IAC3BA,EAAMC,cAAclD,MACtByD,EAASR,EAAMC,cAAclD,OAE7ByD,EAAS,GAAE,GAEZ,IAEGE,EAAaJ,EAAY,EAEzBK,iBACJC,MAACzC,EAAA,CAAU0C,SAAS,OAClBC,wBAAAC,EAAAA,KAACC,SAAMC,QAAS,EAAGC,MAAO,EACvBJ,SAAA,CAAA7D,EAAiBwC,OAAS,kBACzBsB,EAAAA,KAAAI,EAAAA,SAAA,CACGL,SAAA,CAAA7D,EAAiBd,KAAKC,kBACrBwE,EAAAA,IAACQ,GAAgC/E,GAAID,EAAEC,GAAIgF,MAAOjF,EAAEiF,MAAO/E,UAAQ,GAAxCF,EAAEC;iBAE/BuE,IAACU,EAAAA,KAAA,CAAKC,WAAS;eAInBX,EAAAA,IAACY,EAAAA,OAAA,CACCC,KAAK,QACLC,QAAS3B,EACT4B,QAAQ,aACR5E,MAAO6B,EAAc,OAAS,MAC9BgD,WAAYrB,EAEZO,wBAAAC,EAAAA,KAACc,EAAAA,KAAA,CAAKC,IAAK,EAAGC,MAAM,SAClBjB,SAAA;eAAAF,MAACoB,EAAAA,KAAA,CAAKC,KAAM,EACTnB,SAAAlC,uBACEsD,EAAAA,aAAA,CAAaC,KAAK,UACjBrB,wBAAAF,EAAAA,IAACwB,EAAAA,cAAA,qBAGHxB,MAACyB,EAAAA;iBAGLzB,IAACvC,EAAAA,KAAIiE,KAAM,EACTxB,8BAACkB,EAAAA,KAAA,CAAMlB,SAAAlC,EAAc,WAAa,oBAKvC8B,mBACCE,IAAC2B,EAAAA,UAAA,CAAUC,SAAU/B,EAAa1D,MAAOwD,EAAOkC,YAAY,oCAE5D7B,EAAAA,IAACU,EAAAA,KAAA,CAAKC,WAAS,IAGhBhD,EACEzB,QAAQ4F,IACHnC,GACKmC,EAASrB,MAAMsB,cAAczH,SAASqF,EAAMoC,iBAItDxG,KAAKiB,kBACJwD,EAAAA,IAACQ,EAAA,CACC/E,GAAIe,EAAKf,GAETuG,SAAU7D,EACVzC,SAAUqC,EAAgBzD,SAASkC,EAAKf,IACxCgF,MAAOjE,EAAKiE,OAHPjE,EAAKf,WAUhBwG,EACJlE,EAAgBc,SAAWa,EACvB,cACA,WAAW3B,EAAgBc,YAAYa;AAC7C,OACEM,EAAAA,IAACkC,EAAAA,SAAQC,SAAO,EAACpC,UAAkBnC,OAAYwE,QAAM,EAACC,IAAKnD,EACzDgB,wBAAAF,EAAAA,IAACY,EAAAA,OAAA,CACC0B,KAAML,EACNM,KAAMC,EAAAA,cACN3B,KAAK,QACLC,QAASxB,EACT+C,IAAKrD,EACLtD,SAAUkC,KAIlB,CAEA,SAAS4C,EAAqBiC,GAO5B,MAAMhH,GAACA,EAAAuG,SAAIA,EAAAtG,SAAUA,EAAA+E,MAAUA,GAASgC,EAElCC,EAAenE,EAAAA,aAAY,KAC3ByD,GACFA,EAASvG,EAAE,GAEZ,CAACA,EAAIuG,IAEFhB,GAAYgB;AAElB,OACEhC,EAAAA,IAACY,EAAAA,OAAA,CAAOC,KAAK,QAAQC,QAAS4B,EAAc3B,QAAQ,aAAaC,WAC/Dd,wBAAAC,EAAAA,KAACc,EAAAA,KAAA,CAAKC,IAAK,EAAGC,MAAM,SAClBjB,SAAA;eAAAF,EAAAA,IAACoB,EAAAA,MAAKC,KAAM,EACTnB,SAAAxE,uBACE4F,EAAAA,cAAaC,KAAMP,EAAW,UAAY,WACzCd,8BAACyC,EAAAA,oBAAA,CAAA,oBAGH3C,MAAC4C,EAAAA;qBAGJnF,EAAAA,IAAA,CAAIiE,KAAM,EACTxB,wBAAAF,MAACoB,EAAAA,KAAA,CAAMlB;iBAETF,IAAC6C,EAAAA,OAAO3C,SAAAzE,QAIhB,CChLO,SAASqH,EAAoBL,GAClC,MAAOM,QAASC,EAAAxI,WAAaA,EAAAyI,cAAYA,KAAkBC,GAAaT,GAClEtI,oBAACA,EAAAM,QAAqBA,GAAWwC,KACjCN,YAACA,GAAelC,EAChB0I,EAAcV,EAAMtG,MA+B1B,OAAO8G,EAAc,IAAIC,EAAWH,QA7BJ1E,EAAAA,SAAQ,IAC/B2E,EACJ9G,QAAQkH,GAEY,UAAhBA,EAAOC,MACN1G,EAAYnC,EAAY4I,EAAQjJ,EAAqBgJ,IACvC,aAAhBC,EAAOC,MACS,UAAhBD,EAAOC,OAGV9H,KAAK6H,GACgB,aAAhBA,EAAOC,KACF,IACFD,EACHE,SAAU,IACLF,EAAOE,SACVP,QAASK,EAAOE,SAASP,QAAQ7G,QAAQqH,GAEb,UAAxBA,EAAeF,MACf1G,EAAYnC,EAAY+I,EAAgBpJ,EAAqBgJ,OAMhEC,KAEV,CAAC5I,EAAYwI,EAAarG,EAAaxC,EAAqBgJ,IAElB3I,aAAYyI,iBAC3D,CCPO,MAAMnI,EAAiB0I,gBAAoC/I,IAChE,MAAMgJ,EAAwD,mBACrDzD,EAAAA,IAACtC,EAAA,CAAA,GAGJgG,EAAgB,IACjBjH,EAAoBhC,WACpBA,GAGL,MAAO,CACLL,KAAM,0BACNuJ,OAAQ,CACNC,WAAY,CACVC,OAASpB,GJdV,SACLA,GAEA,MAAMqB,EAASC,YAAU,CAACrH,WAAY,gBAC/BsH,EAAWC,GAAgBnG,EAAAA,SAChCxB,MAAMC,QAAQkG,EAAMhI,QAAQ2B,oBAAsBqG,EAAMhI,QAAQ2B,mBAAqB,IAEvF8H,EAAAA,WAAU,KACR,IAAIC,EAA6B,GAO5B7H,MAAMC,QAAQkG,EAAMhI,QAAQ2B,qBALjCgI,eAA4BC,GAC1BF,QAAuBE,EAA2BP,EAAQ,CAAA,GAC1DG,EAAaE,EACf,CAGEG,CAAa7B,EAAMhI,QAAQ2B,mBAAkB,GAE9C,CAAC0H,EAAQrB,EAAMhI,QAAQ2B,qBAE1B,MAAM3B,EAAU4D,EAAAA,SAAiD,KAAA,IAE1D5B,EAAoBhC,WACpBgI,EAAMhI,QACT2B,mBAAoB4H,KAErB,CAACvB,EAAMhI,QAASuJ,KAEZ7J,EAAqByC,GDpCvB,SACLnC,GAEA,OAAOqD,EAAAA,UAAS,IAAM,IAAKrD,EAAQ4B,kBAAoB,MAAQjB,EAAwBX,KACzF,CCgCwD8J,CAAuB9J;AAE7E,OACEuF,EAAAA,IAACjD,EAA4ByH,SAA5B,CACCrI,MAAO,CAAC1B,UAASN,sBAAqByC,0BAErCsD,SAAAuC,EAAMQ,cAAcR,IAG3B,CIvB2BgC,CAA6B,IAAIhC,EAAOhI,QAASiJ,MAIxEgB,SAAU,CACRC,wBAAyB,CAACC,GAAOpK,aAAYS,YACvCV,EAAwBU,EAAO4J,IAAIrK,GAAaC,GAC3C,IAAImK,EAAMnB,GAEZmB,GAIXE,KAAM,CACJlB,WAAY,CACVmB,MAAQtC,GACW,SAAbA,EAAMhH,IAAiBuJ,EAAAA,mBAAmBvC,EAAMjI,YDrEvD,SAA+BiI,GACpC,MAAMhI,QAACA,GAAWwC,IAEZgI,EAAeC,eAAa,CAAC,UAGnC,OAD8B3K,EADf4K,cAC8CN,IAAII,GAAexK,kBACjDuF,EAAAA,IAAC8C,EAAA,IAAwBL,IAAYA,EAAMQ,cAAcR,EAC1F,CC+DmB2C,CAAsB3C,GAGxBA,EAAMQ,cAAcR,KAGjC,IAEH4C,QAAArL,mBAAAA,EAAAqL,QAAA9K,wBAAAA,EAAA8K,QAAAvK,eAAAA,EAAAuK,QAAApI,+BAAAA"}
package/lib/index.mjs CHANGED
@@ -1,3 +1,13 @@
1
- import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import{useState as r,createContext as o,useEffect as l,useMemo as a,useContext as i,useCallback as s}from"react";import{useClient as u,TextWithTone as c,useFormValue as d,useSchema as p,definePlugin as g,isObjectSchemaType as f}from"sanity";import{EyeClosedIcon as m,EyeOpenIcon as y,TranslateIcon as b,CheckmarkCircleIcon as h,CircleIcon as v}from"@sanity/icons";import{Box as L,useClickOutside as O,Stack as w,Card as j,Button as S,Flex as P,Text as T,TextInput as x,Popover as I,Badge as A}from"@sanity/ui";import{styled as D}from"styled-components";const k=(e,t,n)=>!e.name.startsWith("locale")||n.includes(t.name);function C(e,t){var n,r;const o=function(e){return"object"===(null==e?void 0:e.jsonType)&&"document"===F(e).name}(e)&&(null==(n=null==e?void 0:e.options)?void 0:n.languageFilter),l=!t.documentTypes;return!!(l&&!1!==o||!l&&o||e&&null!=(r=t.documentTypes)&&r.includes(e.name))}function F(e){return e.type?F(e.type):e}const N="@sanity/plugin/language-filter/selected-languages";function E(e){const t=z(e).map((e=>e.id));let n=t;try{const e=window.localStorage.getItem(N);e&&(n=JSON.parse(e))}catch(e){}return r=t,n=n.filter((e=>r.includes(e))),n;var r}function z({supportedLanguages:e,defaultLanguages:t}){return Array.isArray(e)?e.filter((e=>!(null!=t&&t.includes(e.id)))):[]}var J=Object.defineProperty,V=Object.defineProperties,$=Object.getOwnPropertyDescriptors,_=Object.getOwnPropertySymbols,H=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable,q=(e,t,n)=>t in e?J(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,B=(e,t)=>{for(var n in t||(t={}))H.call(t,n)&&q(e,n,t[n]);if(_)for(var n of _(t))W.call(t,n)&&q(e,n,t[n]);return e};const G={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:k},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},K=o(G);function M(t){const n=u({apiVersion:"2023-01-01"}),[o,i]=r(Array.isArray(t.options.supportedLanguages)?t.options.supportedLanguages:[]);l((()=>{let e=[];Array.isArray(t.options.supportedLanguages)||async function(t){e=await t(n,{}),i(e)}(t.options.supportedLanguages)}),[n,t.options.supportedLanguages]);const s=a((()=>{return e=B(B({},G.options),t.options),V(e,$({supportedLanguages:o}));var e}),[t.options,o]),[c,d]=function(e){return r((()=>{var t;return[...null!=(t=e.defaultLanguages)?t:[],...E(e)]}))}(s);return e(K.Provider,{value:{options:s,selectedLanguageIds:c,setSelectedLanguageIds:d},children:t.renderDefault(t)})}function Q(){return i(K)}const R=e=>Array.from(new Set(e));function U(){const{selectedLanguageIds:e,setSelectedLanguageIds:t,options:n}=Q(),{defaultLanguages:r=[]}=n,o=a((()=>z(n)),[n]),l=s((e=>{var n;t(R([...r,...e])),n=R([...r,...e]),window.localStorage.setItem(N,JSON.stringify(n))}),[r,t]),i=s((()=>l(o.map((e=>e.id)))),[l,o]),u=s((()=>{l(r)}),[r,l]),c=s((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):R([...n,t]),l(n)}),[l,e]);return{activeLanguages:a((()=>R([...null!=r?r:[],...e])),[r,e]),allSelected:e.length===o.length+r.length,selectAll:i,selectNone:u,toggleLanguage:c}}const X=D(L)`
1
+ import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import{useState as a,createContext as i,useEffect as l,useMemo as r,useContext as o,useCallback as s}from"react";import{useClient as d,TextWithTone as u,useFormValue as c,useSchema as g,definePlugin as p,isObjectSchemaType as f}from"sanity";import{EyeClosedIcon as m,EyeOpenIcon as h,TranslateIcon as L,CheckmarkCircleIcon as y,CircleIcon as S}from"@sanity/icons";import{Box as v,useClickOutside as T,Stack as b,Card as w,Button as A,Flex as x,Text as I,TextInput as k,Popover as C,Badge as F}from"@sanity/ui";import{styled as N}from"styled-components";const j=(e,t,n)=>!e.name.startsWith("locale")||n.includes(t.name);function D(e,t){const n=function(e){return"object"===e?.jsonType&&"document"===z(e).name}(e)&&e?.options?.languageFilter,a=!t.documentTypes;return!!(a&&!1!==n||!a&&n||e&&t.documentTypes?.includes(e.name))}function z(e){return e.type?z(e.type):e}const O="@sanity/plugin/language-filter/selected-languages";function J(e){const t=V(e).map((e=>e.id));let n=t;try{const e=window.localStorage.getItem(O);e&&(n=JSON.parse(e))}catch{}return a=t,n=n.filter((e=>a.includes(e))),n;var a}function V({supportedLanguages:e,defaultLanguages:t}){return Array.isArray(e)?e.filter((e=>!t?.includes(e.id))):[]}const $={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:j},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},_=i($);function E(){return o(_)}const H=e=>Array.from(new Set(e)),P=N(v)`
2
2
  max-height: calc(100vh - 200px);
3
- `;function Y(){const{options:o}=Q(),l=o.supportedLanguages.filter((e=>{var t;return null==(t=o.defaultLanguages)?void 0:t.includes(e.id)})),a=o.supportedLanguages.filter((e=>{var t;return!(null!=(t=o.defaultLanguages)&&t.includes(e.id))})),[i,u]=r(!1),{activeLanguages:d,allSelected:p,selectAll:g,selectNone:f,toggleLanguage:h}=U(),[v,A]=r(null),[D,k]=r(null),C=s((e=>{"ALL"===e.currentTarget.value?g():f()}),[g,f]),F=s((()=>u((e=>!e))),[]),N=s((()=>u(!1)),[]);O(N,[v,D]);const E=o.supportedLanguages.length,[z,J]=r(""),V=s((e=>{e.currentTarget.value?J(e.currentTarget.value):J("")}),[]),$=E>4,_=e(X,{overflow:"auto",children:t(w,{padding:1,space:1,children:[l.length>0&&t(n,{children:[l.map((t=>e(Z,{id:t.id,title:t.title,selected:!0},t.id))),e(j,{borderTop:!0})]}),e(S,{mode:"bleed",onClick:C,justify:"flex-start",value:p?"NONE":"ALL",disabled:!!z,children:t(P,{gap:3,align:"center",children:[e(T,{size:2,children:p?e(c,{tone:"primary",children:e(m,{})}):e(y,{})}),e(L,{flex:1,children:e(T,{children:p?"Hide all":"Show all"})})]})}),$?e(x,{onChange:V,value:z,placeholder:"Filter languages"}):e(j,{borderTop:!0}),a.filter((e=>!z||e.title.toLowerCase().includes(z.toLowerCase()))).map((t=>e(Z,{id:t.id,onToggle:h,selected:d.includes(t.id),title:t.title},t.id)))]})}),H=d.length===E?"Showing all":`Showing ${d.length} / ${E}`;return e(I,{animate:!0,content:_,open:i,portal:!0,ref:k,children:e(S,{text:H,icon:b,mode:"bleed",onClick:F,ref:A,selected:i})})}function Z(n){const{id:r,onToggle:o,selected:l,title:a}=n,i=s((()=>{o&&o(r)}),[r,o]),u=!o;return e(S,{mode:"bleed",onClick:i,justify:"flex-start",disabled:u,children:t(P,{gap:3,align:"center",children:[e(T,{size:2,children:l?e(c,{tone:u?"default":"positive",children:e(h,{})}):e(v,{})}),e(L,{flex:1,children:e(T,{children:a})}),e(A,{children:r})]})})}var ee=Object.defineProperty,te=Object.defineProperties,ne=Object.getOwnPropertyDescriptors,re=Object.getOwnPropertySymbols,oe=Object.prototype.hasOwnProperty,le=Object.prototype.propertyIsEnumerable,ae=(e,t,n)=>t in e?ee(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,ie=(e,t)=>{for(var n in t||(t={}))oe.call(t,n)&&ae(e,n,t[n]);if(re)for(var n of re(t))le.call(t,n)&&ae(e,n,t[n]);return e},se=(e,t)=>te(e,ne(t)),ue=(e,t)=>{var n={};for(var r in e)oe.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&re)for(var r of re(e))t.indexOf(r)<0&&le.call(e,r)&&(n[r]=e[r]);return n};function ce(e){const t=e,{members:n,schemaType:r,renderDefault:o}=t,l=ue(t,["members","schemaType","renderDefault"]),{selectedLanguageIds:i,options:s}=Q(),{filterField:u}=s,c=a((()=>n.filter((e=>"field"===e.kind&&u(r,e,i)||"fieldSet"===e.kind||"error"===e.kind)).map((e=>"fieldSet"===e.kind?se(ie({},e),{fieldSet:se(ie({},e.fieldSet),{members:e.fieldSet.members.filter((e=>"field"===e.kind&&u(r,e,i)))})}):e))),[r,n,u,i]);return o(se(ie({},l),{members:c,schemaType:r,renderDefault:o}))}var de=Object.defineProperty,pe=Object.defineProperties,ge=Object.getOwnPropertyDescriptors,fe=Object.getOwnPropertySymbols,me=Object.prototype.hasOwnProperty,ye=Object.prototype.propertyIsEnumerable,be=(e,t,n)=>t in e?de(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,he=(e,t)=>{for(var n in t||(t={}))me.call(t,n)&&be(e,n,t[n]);if(fe)for(var n of fe(t))ye.call(t,n)&&be(e,n,t[n]);return e};const ve=g((t=>{const n=()=>e(Y,{}),r=he(he({},G.options),t);return{name:"@sanity/language-filter",studio:{components:{layout:e=>{return M((t=he({},e),pe(t,ge({options:r}))));var t}}},document:{unstable_languageFilter:(e,{schemaType:r,schema:o})=>C(o.get(r),t)?[...e,n]:e},form:{components:{input:t=>"root"!==t.id&&f(t.schemaType)?function(t){const{options:n}=Q(),r=d(["_type"]);return C(p().get(r),n)?e(ce,ie({},t)):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));export{k as defaultFilterField,C as isLanguageFilterEnabled,ve as languageFilter,Q as useLanguageFilterStudioContext};//# sourceMappingURL=index.mjs.map
3
+ `;function W(){const{options:i}=E(),l=i.supportedLanguages.filter((e=>i.defaultLanguages?.includes(e.id))),o=i.supportedLanguages.filter((e=>!i.defaultLanguages?.includes(e.id))),[d,c]=a(!1),{activeLanguages:g,allSelected:p,selectAll:f,selectNone:y,toggleLanguage:S}=function(){const{selectedLanguageIds:e,setSelectedLanguageIds:t,options:n}=E(),{defaultLanguages:a=[]}=n,i=r((()=>V(n)),[n]),l=s((e=>{var n;t(H([...a,...e])),n=H([...a,...e]),window.localStorage.setItem(O,JSON.stringify(n))}),[a,t]),o=s((()=>l(i.map((e=>e.id)))),[l,i]),d=s((()=>{l(a)}),[a,l]),u=s((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):H([...n,t]),l(n)}),[l,e]);return{activeLanguages:r((()=>H([...a??[],...e])),[a,e]),allSelected:e.length===i.length+a.length,selectAll:o,selectNone:d,toggleLanguage:u}}(),[F,N]=a(null),[j,D]=a(null),z=s((e=>{"ALL"===e.currentTarget.value?f():y()}),[f,y]),J=s((()=>c((e=>!e))),[]),$=s((()=>c(!1)),[]);T($,[F,j]);const _=i.supportedLanguages.length,[W,B]=a(""),G=s((e=>{e.currentTarget.value?B(e.currentTarget.value):B("")}),[]),K=_>4,M=/* @__PURE__ */e(P,{overflow:"auto",children:/* @__PURE__ */t(b,{padding:1,space:1,children:[l.length>0&&/* @__PURE__ */t(n,{children:[l.map((t=>/* @__PURE__ */e(q,{id:t.id,title:t.title,selected:!0},t.id))),
4
+ /* @__PURE__ */e(w,{borderTop:!0})]}),
5
+ /* @__PURE__ */e(A,{mode:"bleed",onClick:z,justify:"flex-start",value:p?"NONE":"ALL",disabled:!!W,children:/* @__PURE__ */t(x,{gap:3,align:"center",children:[
6
+ /* @__PURE__ */e(I,{size:2,children:p?/* @__PURE__ */e(u,{tone:"primary",children:/* @__PURE__ */e(m,{})}):/* @__PURE__ */e(h,{})}),
7
+ /* @__PURE__ */e(v,{flex:1,children:/* @__PURE__ */e(I,{children:p?"Hide all":"Show all"})})]})}),K?/* @__PURE__ */e(k,{onChange:G,value:W,placeholder:"Filter languages"}):/* @__PURE__ */e(w,{borderTop:!0}),o.filter((e=>!W||e.title.toLowerCase().includes(W.toLowerCase()))).map((t=>/* @__PURE__ */e(q,{id:t.id,onToggle:S,selected:g.includes(t.id),title:t.title},t.id)))]})}),Q=g.length===_?"Showing all":`Showing ${g.length} / ${_}`;/* @__PURE__ */
8
+ return e(C,{animate:!0,content:M,open:d,portal:!0,ref:D,children:/* @__PURE__ */e(A,{text:Q,icon:L,mode:"bleed",onClick:J,ref:N,selected:d})})}function q(n){const{id:a,onToggle:i,selected:l,title:r}=n,o=s((()=>{i&&i(a)}),[a,i]),d=!i;/* @__PURE__ */
9
+ return e(A,{mode:"bleed",onClick:o,justify:"flex-start",disabled:d,children:/* @__PURE__ */t(x,{gap:3,align:"center",children:[
10
+ /* @__PURE__ */e(I,{size:2,children:l?/* @__PURE__ */e(u,{tone:d?"default":"positive",children:/* @__PURE__ */e(y,{})}):/* @__PURE__ */e(S,{})}),
11
+ /* @__PURE__ */e(v,{flex:1,children:/* @__PURE__ */e(I,{children:r})}),
12
+ /* @__PURE__ */e(F,{children:a})]})})}function B(e){const{members:t,schemaType:n,renderDefault:a,...i}=e,{selectedLanguageIds:l,options:o}=E(),{filterField:s}=o,d=e.value;return a({...i,members:r((()=>t.filter((e=>"field"===e.kind&&s(n,e,l,d)||"fieldSet"===e.kind||"error"===e.kind)).map((e=>"fieldSet"===e.kind?{...e,fieldSet:{...e.fieldSet,members:e.fieldSet.members.filter((e=>"field"===e.kind&&s(n,e,l,d)))}}:e))),[n,t,s,l,d]),schemaType:n,renderDefault:a})}const G=p((t=>{const n=()=>/* @__PURE__ */e(W,{}),i={...$.options,...t};return{name:"@sanity/language-filter",studio:{components:{layout:t=>function(t){const n=d({apiVersion:"2023-01-01"}),[i,o]=a(Array.isArray(t.options.supportedLanguages)?t.options.supportedLanguages:[]);l((()=>{let e=[];Array.isArray(t.options.supportedLanguages)||async function(t){e=await t(n,{}),o(e)}(t.options.supportedLanguages)}),[n,t.options.supportedLanguages]);const s=r((()=>({...$.options,...t.options,supportedLanguages:i})),[t.options,i]),[u,c]=function(e){return a((()=>[...e.defaultLanguages??[],...J(e)]))}(s);/* @__PURE__ */
13
+ return e(_.Provider,{value:{options:s,selectedLanguageIds:u,setSelectedLanguageIds:c},children:t.renderDefault(t)})}({...t,options:i})}},document:{unstable_languageFilter:(e,{schemaType:a,schema:i})=>D(i.get(a),t)?[...e,n]:e},form:{components:{input:t=>"root"!==t.id&&f(t.schemaType)?function(t){const{options:n}=E(),a=c(["_type"]);return D(g().get(a),n)?/* @__PURE__ */e(B,{...t}):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));export{j as defaultFilterField,D as isLanguageFilterEnabled,G as languageFilter,E as useLanguageFilterStudioContext};//# sourceMappingURL=index.mjs.map
package/lib/index.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/filterField.ts","../src/useSelectedLanguageIds.ts","../src/LanguageFilterStudioContext.tsx","../src/usePaneLanguages.ts","../src/LanguageFilterMenuButton.tsx","../src/LanguageFilterObjectInput.tsx","../src/plugin.tsx"],"sourcesContent":["import type {SchemaType} from 'sanity'\n\nimport type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'\n\nexport const defaultFilterField: FilterFieldFunction = (\n enclosingType,\n field,\n selectedLanguageIds,\n) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)\n\nexport function isLanguageFilterEnabled(\n schemaType: SchemaType | undefined,\n options: LanguageFilterConfig,\n): boolean {\n const schemaFilter =\n isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter\n const defaultEnabled = !options.documentTypes\n\n return !!(\n (defaultEnabled && schemaFilter !== false) ||\n (!defaultEnabled && schemaFilter) ||\n (schemaType && options.documentTypes?.includes(schemaType.name))\n )\n}\n\nfunction isDocument(schemaType?: SchemaType) {\n return schemaType?.jsonType === 'object' && getRootType(schemaType).name === 'document'\n}\n\nfunction getRootType(schema: SchemaType): SchemaType {\n if (schema.type) {\n return getRootType(schema.type)\n }\n return schema\n}\n","import {useState} from 'react'\n\nimport type {Language, LanguageFilterConfig} from './types'\nconst storageKey = '@sanity/plugin/language-filter/selected-languages'\n\nexport function getPersistedLanguageIds(options: LanguageFilterConfig): string[] {\n const selectableLangs = getSelectableLanguages(options).map((l) => l.id)\n\n let selected: string[] = selectableLangs\n try {\n const persistedValue = window.localStorage.getItem(storageKey)\n if (persistedValue) {\n selected = JSON.parse(persistedValue)\n }\n } catch (err) {} // eslint-disable-line no-empty\n\n // constrain persisted/selected languages to the ones currently supported\n selected = intersection(selected, selectableLangs)\n return selected\n}\n\nexport function persistLanguageIds(languageIds: string[]): void {\n window.localStorage.setItem(storageKey, JSON.stringify(languageIds))\n}\n\nfunction intersection(array1: string[], array2: string[]) {\n return array1.filter((value) => array2.includes(value))\n}\n\nexport function getSelectableLanguages({\n supportedLanguages,\n defaultLanguages,\n}: LanguageFilterConfig): Language[] {\n return Array.isArray(supportedLanguages)\n ? supportedLanguages.filter((lang) => !defaultLanguages?.includes(lang.id))\n : []\n}\n\nexport function useSelectedLanguageIds(\n options: LanguageFilterConfig,\n): [string[], (ids: string[]) => void] {\n return useState(() => [...(options.defaultLanguages ?? []), ...getPersistedLanguageIds(options)])\n}\n","import {createContext, useContext, useEffect, useMemo, useState} from 'react'\nimport {type LayoutProps, useClient} from 'sanity'\n\nimport {defaultFilterField} from './filterField'\nimport type {\n Language,\n LanguageCallback,\n LanguageFilterConfig,\n LanguageFilterConfigProcessed,\n} from './types'\nimport {useSelectedLanguageIds} from './useSelectedLanguageIds'\n\nexport interface LanguageFilterStudioContextProps {\n // eslint-disable-next-line react/require-default-props\n options: Required<LanguageFilterConfig>\n}\n\nexport interface LanguageFilterStudioContextProcessed {\n options: Required<LanguageFilterConfigProcessed>\n}\n\nexport interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {\n selectedLanguageIds: string[]\n setSelectedLanguageIds: (ids: string[]) => void\n}\n\nexport const defaultContextValue: LanguageFilterStudioContextValue = {\n options: {\n apiVersion: '2022-11-27',\n supportedLanguages: [],\n defaultLanguages: [],\n documentTypes: [],\n filterField: defaultFilterField,\n },\n selectedLanguageIds: [],\n setSelectedLanguageIds: () => console.error('LanguageFilterStudioContext not initialized'),\n}\n\nconst LanguageFilterStudioContext =\n createContext<LanguageFilterStudioContextValue>(defaultContextValue)\n\n/**\n * This is a separate Provider from the Context that wraps the document pane\n * but it used to listen to changes to the selected language IDs inside it\n * and provide them to a Studio-wide context\n */\nexport function LanguageFilterStudioProvider(\n props: LayoutProps & LanguageFilterStudioContextProps,\n) {\n const client = useClient({apiVersion: '2023-01-01'})\n const [languages, setLanguages] = useState<Language[]>(\n Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],\n )\n useEffect(() => {\n let asyncLanguages: Language[] = []\n\n async function getLanguages(supportedLanguagesCallback: LanguageCallback) {\n asyncLanguages = await supportedLanguagesCallback(client, {})\n setLanguages(asyncLanguages)\n }\n\n if (!Array.isArray(props.options.supportedLanguages)) {\n getLanguages(props.options.supportedLanguages)\n }\n }, [client, props.options.supportedLanguages])\n\n const options = useMemo<Required<LanguageFilterConfigProcessed>>(() => {\n return {\n ...defaultContextValue.options,\n ...props.options,\n supportedLanguages: languages,\n }\n }, [props.options, languages])\n\n const [selectedLanguageIds, setSelectedLanguageIds] = useSelectedLanguageIds(options)\n\n return (\n <LanguageFilterStudioContext.Provider\n value={{options, selectedLanguageIds, setSelectedLanguageIds}}\n >\n {props.renderDefault(props)}\n </LanguageFilterStudioContext.Provider>\n )\n}\n\n/**\n * Retrieves plugin options and the currently selected\n * language IDs from anywhere in the Studio\n */\nexport function useLanguageFilterStudioContext() {\n return useContext(LanguageFilterStudioContext)\n}\n","import {useCallback, useMemo} from 'react'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'\n\nconst unique = (arr: string[]) => Array.from(new Set(arr))\n\nexport function usePaneLanguages(): {\n activeLanguages: string[]\n allSelected: boolean\n selectAll: () => void\n selectNone: () => void\n toggleLanguage: (languageId: string) => void\n} {\n const {selectedLanguageIds, setSelectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {defaultLanguages = []} = options\n\n const selectableLanguages = useMemo(() => getSelectableLanguages(options), [options])\n\n const updateSelectedIds = useCallback(\n (ids: string[]) => {\n setSelectedLanguageIds(unique([...defaultLanguages, ...ids]))\n persistLanguageIds(unique([...defaultLanguages, ...ids]))\n },\n [defaultLanguages, setSelectedLanguageIds],\n )\n\n const selectAll = useCallback(\n () => updateSelectedIds(selectableLanguages.map((l) => l.id)),\n [updateSelectedIds, selectableLanguages],\n )\n\n const selectNone = useCallback(() => {\n updateSelectedIds(defaultLanguages)\n }, [defaultLanguages, updateSelectedIds])\n\n const toggleLanguage = useCallback(\n (languageId: string) => {\n let lang = selectedLanguageIds\n\n if (lang.includes(languageId)) {\n lang = lang.filter((l) => l !== languageId)\n } else {\n lang = unique([...lang, languageId])\n }\n\n updateSelectedIds(lang)\n },\n [updateSelectedIds, selectedLanguageIds],\n )\n\n const activeLanguages = useMemo(\n () => unique([...(defaultLanguages ?? []), ...selectedLanguageIds]),\n [defaultLanguages, selectedLanguageIds],\n )\n\n return {\n activeLanguages,\n allSelected:\n selectedLanguageIds.length === selectableLanguages.length + defaultLanguages.length,\n selectAll,\n selectNone,\n toggleLanguage,\n }\n}\n","import {\n CheckmarkCircleIcon,\n CircleIcon,\n EyeClosedIcon,\n EyeOpenIcon,\n TranslateIcon,\n} from '@sanity/icons'\nimport {\n Badge,\n Box,\n Button,\n Card,\n Flex,\n Popover,\n Stack,\n Text,\n TextInput,\n useClickOutside,\n} from '@sanity/ui'\nimport {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'\nimport {TextWithTone} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {usePaneLanguages} from './usePaneLanguages'\n\nconst StyledBox = styled(Box)`\n max-height: calc(100vh - 200px);\n`\n\nexport function LanguageFilterMenuButton() {\n const {options} = useLanguageFilterStudioContext()\n\n const defaultLanguages = options.supportedLanguages.filter((l) =>\n options.defaultLanguages?.includes(l.id),\n )\n\n const languageOptions = options.supportedLanguages.filter(\n (l) => !options.defaultLanguages?.includes(l.id),\n )\n const [open, setOpen] = useState(false)\n const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()\n const [button, setButton] = useState<HTMLElement | null>(null)\n const [popover, setPopover] = useState<HTMLElement | null>(null)\n\n const handleToggleAll: MouseEventHandler<HTMLButtonElement> = useCallback(\n (event) => {\n const checked = event.currentTarget.value === 'ALL'\n\n if (checked) {\n selectAll()\n } else {\n selectNone()\n }\n },\n [selectAll, selectNone],\n )\n\n const handleClick = useCallback(() => setOpen((o) => !o), [])\n\n const handleClickOutside = useCallback(() => setOpen(false), [])\n\n useClickOutside(handleClickOutside, [button, popover])\n\n const langCount = options.supportedLanguages.length\n\n // Search filter query\n const [query, setQuery] = useState('')\n const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {\n if (event.currentTarget.value) {\n setQuery(event.currentTarget.value)\n } else {\n setQuery('')\n }\n }, [])\n\n const showSearch = langCount > 4\n\n const content = (\n <StyledBox overflow=\"auto\">\n <Stack padding={1} space={1}>\n {defaultLanguages.length > 0 && (\n <>\n {defaultLanguages.map((l) => (\n <LanguageFilterOption key={l.id} id={l.id} title={l.title} selected />\n ))}\n <Card borderTop />\n </>\n )}\n\n <Button\n mode=\"bleed\"\n onClick={handleToggleAll}\n justify=\"flex-start\"\n value={allSelected ? 'NONE' : 'ALL'}\n disabled={!!query}\n >\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {allSelected ? (\n <TextWithTone tone=\"primary\">\n <EyeClosedIcon />\n </TextWithTone>\n ) : (\n <EyeOpenIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{allSelected ? 'Hide all' : 'Show all'}</Text>\n </Box>\n </Flex>\n </Button>\n\n {showSearch ? (\n <TextInput onChange={handleQuery} value={query} placeholder=\"Filter languages\" />\n ) : (\n <Card borderTop />\n )}\n\n {languageOptions\n .filter((language) => {\n if (query) {\n return language.title.toLowerCase().includes(query.toLowerCase())\n }\n return true\n })\n .map((lang) => (\n <LanguageFilterOption\n id={lang.id}\n key={lang.id}\n onToggle={toggleLanguage}\n selected={activeLanguages.includes(lang.id)}\n title={lang.title}\n />\n ))}\n </Stack>\n </StyledBox>\n )\n\n const buttonText =\n activeLanguages.length === langCount\n ? 'Showing all'\n : `Showing ${activeLanguages.length} / ${langCount}`\n return (\n <Popover animate content={content} open={open} portal ref={setPopover}>\n <Button\n text={buttonText}\n icon={TranslateIcon}\n mode=\"bleed\"\n onClick={handleClick}\n ref={setButton}\n selected={open}\n />\n </Popover>\n )\n}\n\nfunction LanguageFilterOption(props: {\n id: string\n selected: boolean\n title: string\n // eslint-disable-next-line react/require-default-props\n onToggle?: (id: string) => void\n}) {\n const {id, onToggle, selected, title} = props\n\n const handleChange = useCallback(() => {\n if (onToggle) {\n onToggle(id)\n }\n }, [id, onToggle])\n\n const disabled = !onToggle\n\n return (\n <Button mode=\"bleed\" onClick={handleChange} justify=\"flex-start\" disabled={disabled}>\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {selected ? (\n <TextWithTone tone={disabled ? 'default' : 'positive'}>\n <CheckmarkCircleIcon />\n </TextWithTone>\n ) : (\n <CircleIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{title}</Text>\n </Box>\n <Badge>{id}</Badge>\n </Flex>\n </Button>\n )\n}\n","import {useMemo} from 'react'\nimport {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\n\n// First check that this Object is in a schema type for which language-filter is enabled\nexport function FilteredObjectWrapper(props: ObjectInputProps) {\n const {options} = useLanguageFilterStudioContext()\n\n const documentType = useFormValue(['_type']) as string\n const schema = useSchema()\n const languageFilterEnabled = isLanguageFilterEnabled(schema.get(documentType), options)\n return languageFilterEnabled ? <FilteredObjectInput {...props} /> : props.renderDefault(props)\n}\n\n// Modify the object members based on selected languages in the filter\nexport function FilteredObjectInput(props: ObjectInputProps) {\n const {members: membersProp, schemaType, renderDefault, ...restProps} = props\n const {selectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {filterField} = options\n\n const members: ObjectMember[] = useMemo(() => {\n return membersProp\n .filter((member) => {\n return (\n (member.kind === 'field' && filterField(schemaType, member, selectedLanguageIds)) ||\n member.kind === 'fieldSet' ||\n member.kind === 'error'\n )\n })\n .map((member) => {\n if (member.kind === 'fieldSet') {\n return {\n ...member,\n fieldSet: {\n ...member.fieldSet,\n members: member.fieldSet.members.filter((fieldsetMember) => {\n return (\n fieldsetMember.kind === 'field' &&\n filterField(schemaType, fieldsetMember, selectedLanguageIds)\n )\n }),\n },\n }\n }\n return member\n })\n }, [schemaType, membersProp, filterField, selectedLanguageIds])\n\n return renderDefault({...restProps, members, schemaType, renderDefault})\n}\n","import {\n definePlugin,\n type DocumentLanguageFilterComponent,\n isObjectSchemaType,\n type ObjectInputProps,\n} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {LanguageFilterMenuButton} from './LanguageFilterMenuButton'\nimport {FilteredObjectWrapper} from './LanguageFilterObjectInput'\nimport {defaultContextValue, LanguageFilterStudioProvider} from './LanguageFilterStudioContext'\nimport type {LanguageFilterConfig} from './types'\n\n/**\n * ## Usage in sanity.config.ts (or .js)\n *\n * ```\n * import {defineConfig} from 'sanity'\n * import {languageFilter} from '@sanity/language-filter'\n *\n * export const defineConfig({\n * /...\n * plugins: [\n * languageFilter({\n * supportedLanguages: [\n * {id: 'nb', title: 'Norwegian (Bokmål)'},\n * {id: 'nn', title: 'Norwegian (Nynorsk)'},\n * {id: 'en', title: 'English'},\n * {id: 'es', title: 'Spanish'},\n * {id: 'arb', title: 'Arabic'},\n * {id: 'pt', title: 'Portuguese'},\n * //...\n * ],\n * // Select Norwegian (Bokmål) by default\n * defaultLanguages: ['nb'],\n * // Only show language filter for document type `page` (schemaType.name)\n * // Can also enable via document-options: options.languageFilter: true\n * documentTypes: ['page'],\n * // default filter function shown\n * filterField: (enclosingType, field, selectedLanguageIds) =>\n * !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name),\n * })\n * ]\n * })\n * ```\n */\nexport const languageFilter = definePlugin<LanguageFilterConfig>((options) => {\n const RenderLanguageFilter: DocumentLanguageFilterComponent = () => {\n return <LanguageFilterMenuButton />\n }\n\n const pluginOptions = {\n ...defaultContextValue.options,\n ...options,\n }\n\n return {\n name: '@sanity/language-filter',\n studio: {\n components: {\n layout: (props) => LanguageFilterStudioProvider({...props, options: pluginOptions}),\n },\n },\n\n document: {\n unstable_languageFilter: (prev, {schemaType, schema}) => {\n if (isLanguageFilterEnabled(schema.get(schemaType), options)) {\n return [...prev, RenderLanguageFilter]\n }\n return prev\n },\n },\n\n form: {\n components: {\n input: (props) => {\n if (props.id !== 'root' && isObjectSchemaType(props.schemaType)) {\n return FilteredObjectWrapper(props as ObjectInputProps)\n }\n\n return props.renderDefault(props)\n },\n },\n },\n }\n})\n"],"names":["defaultFilterField","enclosingType","field","selectedLanguageIds","name","startsWith","includes","isLanguageFilterEnabled","schemaType","options","_a","_b","schemaFilter","jsonType","getRootType","isDocument","languageFilter","defaultEnabled","documentTypes","schema","type","storageKey","getPersistedLanguageIds","selectableLangs","getSelectableLanguages","map","l","id","selected","persistedValue","window","localStorage","getItem","JSON","parse","err","array2","filter","value","supportedLanguages","defaultLanguages","Array","isArray","lang","defaultContextValue","apiVersion","filterField","setSelectedLanguageIds","console","error","LanguageFilterStudioContext","createContext","LanguageFilterStudioProvider","props","client","useClient","languages","setLanguages","useState","useEffect","asyncLanguages","async","supportedLanguagesCallback","getLanguages","useMemo","__spreadProps","useSelectedLanguageIds","jsx","Provider","children","renderDefault","useLanguageFilterStudioContext","useContext","unique","arr","from","Set","usePaneLanguages","selectableLanguages","updateSelectedIds","useCallback","ids","languageIds","setItem","stringify","selectAll","selectNone","toggleLanguage","languageId","activeLanguages","allSelected","length","StyledBox","styled","Box","LanguageFilterMenuButton","languageOptions","open","setOpen","button","setButton","popover","setPopover","handleToggleAll","event","currentTarget","handleClick","o","handleClickOutside","useClickOutside","langCount","query","setQuery","handleQuery","showSearch","content","overflow","jsxs","Stack","padding","space","Fragment","LanguageFilterOption","title","Card","borderTop","Button","mode","onClick","justify","disabled","Flex","gap","align","Text","size","TextWithTone","tone","EyeClosedIcon","EyeOpenIcon","flex","TextInput","onChange","placeholder","language","toLowerCase","onToggle","buttonText","Popover","animate","portal","ref","text","icon","TranslateIcon","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","restProps","__objRest","member","kind","__spreadValues","fieldSet","fieldsetMember","definePlugin","RenderLanguageFilter","pluginOptions","studio","components","layout","document","unstable_languageFilter","prev","get","form","input","isObjectSchemaType","documentType","useFormValue","useSchema","FilteredObjectWrapper"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/filterField.ts","../src/useSelectedLanguageIds.ts","../src/LanguageFilterStudioContext.tsx","../src/usePaneLanguages.ts","../src/LanguageFilterMenuButton.tsx","../src/LanguageFilterObjectInput.tsx","../src/plugin.tsx"],"sourcesContent":["import type {SchemaType} from 'sanity'\n\nimport type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'\n\nexport const defaultFilterField: FilterFieldFunction = (\n enclosingType,\n field,\n selectedLanguageIds,\n) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)\n\nexport function isLanguageFilterEnabled(\n schemaType: SchemaType | undefined,\n options: LanguageFilterConfig,\n): boolean {\n const schemaFilter =\n isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter\n const defaultEnabled = !options.documentTypes\n\n return !!(\n (defaultEnabled && schemaFilter !== false) ||\n (!defaultEnabled && schemaFilter) ||\n (schemaType && options.documentTypes?.includes(schemaType.name))\n )\n}\n\nfunction isDocument(schemaType?: SchemaType) {\n return schemaType?.jsonType === 'object' && getRootType(schemaType).name === 'document'\n}\n\nfunction getRootType(schema: SchemaType): SchemaType {\n if (schema.type) {\n return getRootType(schema.type)\n }\n return schema\n}\n","import {useState} from 'react'\n\nimport type {Language, LanguageFilterConfig} from './types'\nconst storageKey = '@sanity/plugin/language-filter/selected-languages'\n\nexport function getPersistedLanguageIds(options: LanguageFilterConfig): string[] {\n const selectableLangs = getSelectableLanguages(options).map((l) => l.id)\n\n let selected: string[] = selectableLangs\n try {\n const persistedValue = window.localStorage.getItem(storageKey)\n if (persistedValue) {\n selected = JSON.parse(persistedValue)\n }\n } catch (err) {} // eslint-disable-line no-empty\n\n // constrain persisted/selected languages to the ones currently supported\n selected = intersection(selected, selectableLangs)\n return selected\n}\n\nexport function persistLanguageIds(languageIds: string[]): void {\n window.localStorage.setItem(storageKey, JSON.stringify(languageIds))\n}\n\nfunction intersection(array1: string[], array2: string[]) {\n return array1.filter((value) => array2.includes(value))\n}\n\nexport function getSelectableLanguages({\n supportedLanguages,\n defaultLanguages,\n}: LanguageFilterConfig): Language[] {\n return Array.isArray(supportedLanguages)\n ? supportedLanguages.filter((lang) => !defaultLanguages?.includes(lang.id))\n : []\n}\n\nexport function useSelectedLanguageIds(\n options: LanguageFilterConfig,\n): [string[], (ids: string[]) => void] {\n return useState(() => [...(options.defaultLanguages ?? []), ...getPersistedLanguageIds(options)])\n}\n","import {createContext, useContext, useEffect, useMemo, useState} from 'react'\nimport {type LayoutProps, useClient} from 'sanity'\n\nimport {defaultFilterField} from './filterField'\nimport type {\n Language,\n LanguageCallback,\n LanguageFilterConfig,\n LanguageFilterConfigProcessed,\n} from './types'\nimport {useSelectedLanguageIds} from './useSelectedLanguageIds'\n\nexport interface LanguageFilterStudioContextProps {\n // eslint-disable-next-line react/require-default-props\n options: Required<LanguageFilterConfig>\n}\n\nexport interface LanguageFilterStudioContextProcessed {\n options: Required<LanguageFilterConfigProcessed>\n}\n\nexport interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {\n selectedLanguageIds: string[]\n setSelectedLanguageIds: (ids: string[]) => void\n}\n\nexport const defaultContextValue: LanguageFilterStudioContextValue = {\n options: {\n apiVersion: '2022-11-27',\n supportedLanguages: [],\n defaultLanguages: [],\n documentTypes: [],\n filterField: defaultFilterField,\n },\n selectedLanguageIds: [],\n setSelectedLanguageIds: () => console.error('LanguageFilterStudioContext not initialized'),\n}\n\nconst LanguageFilterStudioContext =\n createContext<LanguageFilterStudioContextValue>(defaultContextValue)\n\n/**\n * This is a separate Provider from the Context that wraps the document pane\n * but it used to listen to changes to the selected language IDs inside it\n * and provide them to a Studio-wide context\n */\nexport function LanguageFilterStudioProvider(\n props: LayoutProps & LanguageFilterStudioContextProps,\n) {\n const client = useClient({apiVersion: '2023-01-01'})\n const [languages, setLanguages] = useState<Language[]>(\n Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],\n )\n useEffect(() => {\n let asyncLanguages: Language[] = []\n\n async function getLanguages(supportedLanguagesCallback: LanguageCallback) {\n asyncLanguages = await supportedLanguagesCallback(client, {})\n setLanguages(asyncLanguages)\n }\n\n if (!Array.isArray(props.options.supportedLanguages)) {\n getLanguages(props.options.supportedLanguages)\n }\n }, [client, props.options.supportedLanguages])\n\n const options = useMemo<Required<LanguageFilterConfigProcessed>>(() => {\n return {\n ...defaultContextValue.options,\n ...props.options,\n supportedLanguages: languages,\n }\n }, [props.options, languages])\n\n const [selectedLanguageIds, setSelectedLanguageIds] = useSelectedLanguageIds(options)\n\n return (\n <LanguageFilterStudioContext.Provider\n value={{options, selectedLanguageIds, setSelectedLanguageIds}}\n >\n {props.renderDefault(props)}\n </LanguageFilterStudioContext.Provider>\n )\n}\n\n/**\n * Retrieves plugin options and the currently selected\n * language IDs from anywhere in the Studio\n */\nexport function useLanguageFilterStudioContext() {\n return useContext(LanguageFilterStudioContext)\n}\n","import {useCallback, useMemo} from 'react'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'\n\nconst unique = (arr: string[]) => Array.from(new Set(arr))\n\nexport function usePaneLanguages(): {\n activeLanguages: string[]\n allSelected: boolean\n selectAll: () => void\n selectNone: () => void\n toggleLanguage: (languageId: string) => void\n} {\n const {selectedLanguageIds, setSelectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {defaultLanguages = []} = options\n\n const selectableLanguages = useMemo(() => getSelectableLanguages(options), [options])\n\n const updateSelectedIds = useCallback(\n (ids: string[]) => {\n setSelectedLanguageIds(unique([...defaultLanguages, ...ids]))\n persistLanguageIds(unique([...defaultLanguages, ...ids]))\n },\n [defaultLanguages, setSelectedLanguageIds],\n )\n\n const selectAll = useCallback(\n () => updateSelectedIds(selectableLanguages.map((l) => l.id)),\n [updateSelectedIds, selectableLanguages],\n )\n\n const selectNone = useCallback(() => {\n updateSelectedIds(defaultLanguages)\n }, [defaultLanguages, updateSelectedIds])\n\n const toggleLanguage = useCallback(\n (languageId: string) => {\n let lang = selectedLanguageIds\n\n if (lang.includes(languageId)) {\n lang = lang.filter((l) => l !== languageId)\n } else {\n lang = unique([...lang, languageId])\n }\n\n updateSelectedIds(lang)\n },\n [updateSelectedIds, selectedLanguageIds],\n )\n\n const activeLanguages = useMemo(\n () => unique([...(defaultLanguages ?? []), ...selectedLanguageIds]),\n [defaultLanguages, selectedLanguageIds],\n )\n\n return {\n activeLanguages,\n allSelected:\n selectedLanguageIds.length === selectableLanguages.length + defaultLanguages.length,\n selectAll,\n selectNone,\n toggleLanguage,\n }\n}\n","import {\n CheckmarkCircleIcon,\n CircleIcon,\n EyeClosedIcon,\n EyeOpenIcon,\n TranslateIcon,\n} from '@sanity/icons'\nimport {\n Badge,\n Box,\n Button,\n Card,\n Flex,\n Popover,\n Stack,\n Text,\n TextInput,\n useClickOutside,\n} from '@sanity/ui'\nimport {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'\nimport {TextWithTone} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {usePaneLanguages} from './usePaneLanguages'\n\nconst StyledBox = styled(Box)`\n max-height: calc(100vh - 200px);\n`\n\nexport function LanguageFilterMenuButton() {\n const {options} = useLanguageFilterStudioContext()\n\n const defaultLanguages = options.supportedLanguages.filter((l) =>\n options.defaultLanguages?.includes(l.id),\n )\n\n const languageOptions = options.supportedLanguages.filter(\n (l) => !options.defaultLanguages?.includes(l.id),\n )\n const [open, setOpen] = useState(false)\n const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()\n const [button, setButton] = useState<HTMLElement | null>(null)\n const [popover, setPopover] = useState<HTMLElement | null>(null)\n\n const handleToggleAll: MouseEventHandler<HTMLButtonElement> = useCallback(\n (event) => {\n const checked = event.currentTarget.value === 'ALL'\n\n if (checked) {\n selectAll()\n } else {\n selectNone()\n }\n },\n [selectAll, selectNone],\n )\n\n const handleClick = useCallback(() => setOpen((o) => !o), [])\n\n const handleClickOutside = useCallback(() => setOpen(false), [])\n\n useClickOutside(handleClickOutside, [button, popover])\n\n const langCount = options.supportedLanguages.length\n\n // Search filter query\n const [query, setQuery] = useState('')\n const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {\n if (event.currentTarget.value) {\n setQuery(event.currentTarget.value)\n } else {\n setQuery('')\n }\n }, [])\n\n const showSearch = langCount > 4\n\n const content = (\n <StyledBox overflow=\"auto\">\n <Stack padding={1} space={1}>\n {defaultLanguages.length > 0 && (\n <>\n {defaultLanguages.map((l) => (\n <LanguageFilterOption key={l.id} id={l.id} title={l.title} selected />\n ))}\n <Card borderTop />\n </>\n )}\n\n <Button\n mode=\"bleed\"\n onClick={handleToggleAll}\n justify=\"flex-start\"\n value={allSelected ? 'NONE' : 'ALL'}\n disabled={!!query}\n >\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {allSelected ? (\n <TextWithTone tone=\"primary\">\n <EyeClosedIcon />\n </TextWithTone>\n ) : (\n <EyeOpenIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{allSelected ? 'Hide all' : 'Show all'}</Text>\n </Box>\n </Flex>\n </Button>\n\n {showSearch ? (\n <TextInput onChange={handleQuery} value={query} placeholder=\"Filter languages\" />\n ) : (\n <Card borderTop />\n )}\n\n {languageOptions\n .filter((language) => {\n if (query) {\n return language.title.toLowerCase().includes(query.toLowerCase())\n }\n return true\n })\n .map((lang) => (\n <LanguageFilterOption\n id={lang.id}\n key={lang.id}\n onToggle={toggleLanguage}\n selected={activeLanguages.includes(lang.id)}\n title={lang.title}\n />\n ))}\n </Stack>\n </StyledBox>\n )\n\n const buttonText =\n activeLanguages.length === langCount\n ? 'Showing all'\n : `Showing ${activeLanguages.length} / ${langCount}`\n return (\n <Popover animate content={content} open={open} portal ref={setPopover}>\n <Button\n text={buttonText}\n icon={TranslateIcon}\n mode=\"bleed\"\n onClick={handleClick}\n ref={setButton}\n selected={open}\n />\n </Popover>\n )\n}\n\nfunction LanguageFilterOption(props: {\n id: string\n selected: boolean\n title: string\n // eslint-disable-next-line react/require-default-props\n onToggle?: (id: string) => void\n}) {\n const {id, onToggle, selected, title} = props\n\n const handleChange = useCallback(() => {\n if (onToggle) {\n onToggle(id)\n }\n }, [id, onToggle])\n\n const disabled = !onToggle\n\n return (\n <Button mode=\"bleed\" onClick={handleChange} justify=\"flex-start\" disabled={disabled}>\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {selected ? (\n <TextWithTone tone={disabled ? 'default' : 'positive'}>\n <CheckmarkCircleIcon />\n </TextWithTone>\n ) : (\n <CircleIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{title}</Text>\n </Box>\n <Badge>{id}</Badge>\n </Flex>\n </Button>\n )\n}\n","import {useMemo} from 'react'\nimport {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\n\n// First check that this Object is in a schema type for which language-filter is enabled\nexport function FilteredObjectWrapper(props: ObjectInputProps) {\n const {options} = useLanguageFilterStudioContext()\n\n const documentType = useFormValue(['_type']) as string\n const schema = useSchema()\n const languageFilterEnabled = isLanguageFilterEnabled(schema.get(documentType), options)\n return languageFilterEnabled ? <FilteredObjectInput {...props} /> : props.renderDefault(props)\n}\n\n// Modify the object members based on selected languages in the filter\nexport function FilteredObjectInput(props: ObjectInputProps) {\n const {members: membersProp, schemaType, renderDefault, ...restProps} = props\n const {selectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {filterField} = options\n const parentValue = props.value\n\n const members: ObjectMember[] = useMemo(() => {\n return membersProp\n .filter((member) => {\n return (\n (member.kind === 'field' &&\n filterField(schemaType, member, selectedLanguageIds, parentValue)) ||\n member.kind === 'fieldSet' ||\n member.kind === 'error'\n )\n })\n .map((member) => {\n if (member.kind === 'fieldSet') {\n return {\n ...member,\n fieldSet: {\n ...member.fieldSet,\n members: member.fieldSet.members.filter((fieldsetMember) => {\n return (\n fieldsetMember.kind === 'field' &&\n filterField(schemaType, fieldsetMember, selectedLanguageIds, parentValue)\n )\n }),\n },\n }\n }\n return member\n })\n }, [schemaType, membersProp, filterField, selectedLanguageIds, parentValue])\n\n return renderDefault({...restProps, members, schemaType, renderDefault})\n}\n","import {\n definePlugin,\n type DocumentLanguageFilterComponent,\n isObjectSchemaType,\n type ObjectInputProps,\n} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {LanguageFilterMenuButton} from './LanguageFilterMenuButton'\nimport {FilteredObjectWrapper} from './LanguageFilterObjectInput'\nimport {defaultContextValue, LanguageFilterStudioProvider} from './LanguageFilterStudioContext'\nimport type {LanguageFilterConfig} from './types'\n\n/**\n * ## Usage in sanity.config.ts (or .js)\n *\n * ```\n * import {defineConfig} from 'sanity'\n * import {languageFilter} from '@sanity/language-filter'\n *\n * export const defineConfig({\n * /...\n * plugins: [\n * languageFilter({\n * supportedLanguages: [\n * {id: 'nb', title: 'Norwegian (Bokmål)'},\n * {id: 'nn', title: 'Norwegian (Nynorsk)'},\n * {id: 'en', title: 'English'},\n * {id: 'es', title: 'Spanish'},\n * {id: 'arb', title: 'Arabic'},\n * {id: 'pt', title: 'Portuguese'},\n * //...\n * ],\n * // Select Norwegian (Bokmål) by default\n * defaultLanguages: ['nb'],\n * // Only show language filter for document type `page` (schemaType.name)\n * // Can also enable via document-options: options.languageFilter: true\n * documentTypes: ['page'],\n * // default filter function shown\n * filterField: (enclosingType, field, selectedLanguageIds) =>\n * !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name),\n * })\n * ]\n * })\n * ```\n */\nexport const languageFilter = definePlugin<LanguageFilterConfig>((options) => {\n const RenderLanguageFilter: DocumentLanguageFilterComponent = () => {\n return <LanguageFilterMenuButton />\n }\n\n const pluginOptions = {\n ...defaultContextValue.options,\n ...options,\n }\n\n return {\n name: '@sanity/language-filter',\n studio: {\n components: {\n layout: (props) => LanguageFilterStudioProvider({...props, options: pluginOptions}),\n },\n },\n\n document: {\n unstable_languageFilter: (prev, {schemaType, schema}) => {\n if (isLanguageFilterEnabled(schema.get(schemaType), options)) {\n return [...prev, RenderLanguageFilter]\n }\n return prev\n },\n },\n\n form: {\n components: {\n input: (props) => {\n if (props.id !== 'root' && isObjectSchemaType(props.schemaType)) {\n return FilteredObjectWrapper(props as ObjectInputProps)\n }\n\n return props.renderDefault(props)\n },\n },\n },\n }\n})\n"],"names":["defaultFilterField","enclosingType","field","selectedLanguageIds","name","startsWith","includes","isLanguageFilterEnabled","schemaType","options","schemaFilter","jsonType","getRootType","isDocument","languageFilter","defaultEnabled","documentTypes","schema","type","storageKey","getPersistedLanguageIds","selectableLangs","getSelectableLanguages","map","l","id","selected","persistedValue","window","localStorage","getItem","JSON","parse","array2","filter","value","supportedLanguages","defaultLanguages","Array","isArray","lang","defaultContextValue","apiVersion","filterField","setSelectedLanguageIds","console","error","LanguageFilterStudioContext","createContext","useLanguageFilterStudioContext","useContext","unique","arr","from","Set","StyledBox","styled","Box","LanguageFilterMenuButton","languageOptions","open","setOpen","useState","activeLanguages","allSelected","selectAll","selectNone","toggleLanguage","selectableLanguages","useMemo","updateSelectedIds","useCallback","ids","languageIds","setItem","stringify","languageId","length","usePaneLanguages","button","setButton","popover","setPopover","handleToggleAll","event","currentTarget","handleClick","o","handleClickOutside","useClickOutside","langCount","query","setQuery","handleQuery","showSearch","content","jsx","overflow","children","jsxs","Stack","padding","space","Fragment","LanguageFilterOption","title","Card","borderTop","Button","mode","onClick","justify","disabled","Flex","gap","align","Text","size","TextWithTone","tone","EyeClosedIcon","EyeOpenIcon","flex","TextInput","onChange","placeholder","language","toLowerCase","onToggle","buttonText","Popover","animate","portal","ref","text","icon","TranslateIcon","props","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","renderDefault","restProps","parentValue","member","kind","fieldSet","fieldsetMember","definePlugin","RenderLanguageFilter","pluginOptions","studio","components","layout","client","useClient","languages","setLanguages","useEffect","asyncLanguages","async","supportedLanguagesCallback","getLanguages","useSelectedLanguageIds","Provider","LanguageFilterStudioProvider","document","unstable_languageFilter","prev","get","form","input","isObjectSchemaType","documentType","useFormValue","useSchema","FilteredObjectWrapper"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/language-filter",
3
- "version": "4.0.5",
3
+ "version": "4.1.0",
4
4
  "description": "A Sanity plugin that supports filtering localized fields by language",
5
5
  "homepage": "https://github.com/sanity-io/language-filter#readme",
6
6
  "bugs": {
@@ -24,7 +24,6 @@
24
24
  },
25
25
  "main": "./lib/index.js",
26
26
  "module": "./lib/index.esm.js",
27
- "source": "./src/index.ts",
28
27
  "types": "./lib/index.d.ts",
29
28
  "files": [
30
29
  "src",
@@ -46,8 +45,8 @@
46
45
  "dependencies": {
47
46
  "@sanity/icons": "^3.5.3",
48
47
  "@sanity/incompatible-plugin": "^1.0.5",
49
- "@sanity/ui": "^2.10.11",
50
- "@sanity/util": "^3.67.1"
48
+ "@sanity/ui": "^3.1.11",
49
+ "@sanity/util": "^5.0.1"
51
50
  },
52
51
  "devDependencies": {
53
52
  "@babel/preset-env": "^7.24.4",
@@ -72,6 +71,7 @@
72
71
  "prettier": "^3.2.5",
73
72
  "prettier-plugin-packagejson": "^2.4.14",
74
73
  "react": "^18.3.1",
74
+ "react-dom": "^18.3.1",
75
75
  "rimraf": "^4.4.1",
76
76
  "sanity": "^3.67.1",
77
77
  "styled-components": "^6.1.8",
@@ -80,7 +80,7 @@
80
80
  },
81
81
  "peerDependencies": {
82
82
  "react": "^18 || ^19",
83
- "sanity": "^3.36.4 || ^4.0.0-0",
83
+ "sanity": "^3.36.4 || ^4.0.0-0 || ^5",
84
84
  "styled-components": "^6.1"
85
85
  },
86
86
  "engines": {
@@ -19,12 +19,14 @@ export function FilteredObjectInput(props: ObjectInputProps) {
19
19
  const {members: membersProp, schemaType, renderDefault, ...restProps} = props
20
20
  const {selectedLanguageIds, options} = useLanguageFilterStudioContext()
21
21
  const {filterField} = options
22
+ const parentValue = props.value
22
23
 
23
24
  const members: ObjectMember[] = useMemo(() => {
24
25
  return membersProp
25
26
  .filter((member) => {
26
27
  return (
27
- (member.kind === 'field' && filterField(schemaType, member, selectedLanguageIds)) ||
28
+ (member.kind === 'field' &&
29
+ filterField(schemaType, member, selectedLanguageIds, parentValue)) ||
28
30
  member.kind === 'fieldSet' ||
29
31
  member.kind === 'error'
30
32
  )
@@ -38,7 +40,7 @@ export function FilteredObjectInput(props: ObjectInputProps) {
38
40
  members: member.fieldSet.members.filter((fieldsetMember) => {
39
41
  return (
40
42
  fieldsetMember.kind === 'field' &&
41
- filterField(schemaType, fieldsetMember, selectedLanguageIds)
43
+ filterField(schemaType, fieldsetMember, selectedLanguageIds, parentValue)
42
44
  )
43
45
  }),
44
46
  },
@@ -46,7 +48,7 @@ export function FilteredObjectInput(props: ObjectInputProps) {
46
48
  }
47
49
  return member
48
50
  })
49
- }, [schemaType, membersProp, filterField, selectedLanguageIds])
51
+ }, [schemaType, membersProp, filterField, selectedLanguageIds, parentValue])
50
52
 
51
53
  return renderDefault({...restProps, members, schemaType, renderDefault})
52
54
  }
@@ -79,12 +79,18 @@ describe('filterField', () => {
79
79
  }
80
80
 
81
81
  it('should filter -> true for nb field inside local-prefixed object', () => {
82
- const result = defaultFilterField(localePrefixedObject, member, ['nb'])
82
+ const result = defaultFilterField(localePrefixedObject, member, ['nb'], {
83
+ _key: 'nb',
84
+ _type: 'locale_parent',
85
+ })
83
86
  expect(result).toBeTruthy()
84
87
  })
85
88
 
86
89
  it('should filter -> false for unselected field inside local-prefixed object', () => {
87
- const result = defaultFilterField(localePrefixedObject, member, ['other'])
90
+ const result = defaultFilterField(localePrefixedObject, member, ['other'], {
91
+ _key: 'other',
92
+ _type: 'locale_parent',
93
+ })
88
94
  expect(result).toBeFalsy()
89
95
  })
90
96
 
@@ -93,6 +99,10 @@ describe('filterField', () => {
93
99
  {...localePrefixedObject, name: 'not-start-with-locale-field'},
94
100
  member,
95
101
  ['nb'],
102
+ {
103
+ _key: 'nb',
104
+ _type: 'not-start-with-locale-field',
105
+ },
96
106
  )
97
107
  expect(result).toBeTruthy()
98
108
  })
package/src/types.ts CHANGED
@@ -22,6 +22,7 @@ export type FilterFieldFunction = (
22
22
  enclosingType: ObjectSchemaType,
23
23
  field: FieldMember | FieldsetState,
24
24
  selectedLanguageIds: string[],
25
+ parentValue: Record<string, unknown> | undefined,
25
26
  ) => boolean
26
27
 
27
28
  export interface LanguageFilterConfig {