@sanity/language-filter 3.2.1 → 4.0.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 +1 -1
- package/lib/index.d.mts +107 -0
- package/lib/{src/index.d.ts → index.d.ts} +7 -7
- package/lib/index.esm.js +3 -1
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +3 -0
- package/lib/index.mjs.map +1 -0
- package/package.json +44 -39
- package/src/LanguageFilterMenuButton.tsx +19 -18
- package/src/LanguageFilterObjectInput.tsx +4 -3
- package/src/LanguageFilterStudioContext.tsx +7 -6
- package/src/filterField.test.ts +8 -5
- package/src/filterField.ts +4 -3
- package/src/getSelectedValue.ts +2 -2
- package/src/index.ts +5 -8
- package/src/plugin.tsx +6 -6
- package/src/types.ts +3 -3
- package/src/usePaneLanguages.ts +5 -5
- package/src/useSelectedLanguageIds.ts +3 -2
package/LICENSE
CHANGED
package/lib/index.d.mts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
|
|
3
|
+
import type {FieldMember} from 'sanity'
|
|
4
|
+
import type {FieldsetState} from 'sanity'
|
|
5
|
+
import type {ObjectSchemaType} from 'sanity'
|
|
6
|
+
import {Plugin as Plugin_2} from 'sanity'
|
|
7
|
+
import type {SanityClient} from 'sanity'
|
|
8
|
+
import type {SchemaType} from 'sanity'
|
|
9
|
+
|
|
10
|
+
export declare const defaultFilterField: FilterFieldFunction
|
|
11
|
+
|
|
12
|
+
export declare type FilterFieldFunction = (
|
|
13
|
+
enclosingType: ObjectSchemaType,
|
|
14
|
+
field: FieldMember | FieldsetState,
|
|
15
|
+
selectedLanguageIds: string[],
|
|
16
|
+
) => boolean
|
|
17
|
+
|
|
18
|
+
export declare function isLanguageFilterEnabled(
|
|
19
|
+
schemaType: SchemaType | undefined,
|
|
20
|
+
options: LanguageFilterConfig,
|
|
21
|
+
): boolean
|
|
22
|
+
|
|
23
|
+
export declare type Language = {
|
|
24
|
+
id: Intl.UnicodeBCP47LocaleIdentifier
|
|
25
|
+
title: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare type LanguageCallback = (
|
|
29
|
+
client: SanityClient,
|
|
30
|
+
selectedValue: Record<string, unknown>,
|
|
31
|
+
) => Promise<Language[]>
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* ## Usage in sanity.config.ts (or .js)
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* import {defineConfig} from 'sanity'
|
|
38
|
+
* import {languageFilter} from '@sanity/language-filter'
|
|
39
|
+
*
|
|
40
|
+
* export const defineConfig({
|
|
41
|
+
* /...
|
|
42
|
+
* plugins: [
|
|
43
|
+
* languageFilter({
|
|
44
|
+
* supportedLanguages: [
|
|
45
|
+
* {id: 'nb', title: 'Norwegian (Bokmål)'},
|
|
46
|
+
* {id: 'nn', title: 'Norwegian (Nynorsk)'},
|
|
47
|
+
* {id: 'en', title: 'English'},
|
|
48
|
+
* {id: 'es', title: 'Spanish'},
|
|
49
|
+
* {id: 'arb', title: 'Arabic'},
|
|
50
|
+
* {id: 'pt', title: 'Portuguese'},
|
|
51
|
+
* //...
|
|
52
|
+
* ],
|
|
53
|
+
* // Select Norwegian (Bokmål) by default
|
|
54
|
+
* defaultLanguages: ['nb'],
|
|
55
|
+
* // Only show language filter for document type `page` (schemaType.name)
|
|
56
|
+
* // Can also enable via document-options: options.languageFilter: true
|
|
57
|
+
* documentTypes: ['page'],
|
|
58
|
+
* // default filter function shown
|
|
59
|
+
* filterField: (enclosingType, field, selectedLanguageIds) =>
|
|
60
|
+
* !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name),
|
|
61
|
+
* })
|
|
62
|
+
* ]
|
|
63
|
+
* })
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare const languageFilter: Plugin_2<LanguageFilterConfig>
|
|
67
|
+
|
|
68
|
+
export declare interface LanguageFilterConfig {
|
|
69
|
+
supportedLanguages: Language[] | LanguageCallback
|
|
70
|
+
defaultLanguages?: string[]
|
|
71
|
+
documentTypes?: string[]
|
|
72
|
+
filterField?: FilterFieldFunction
|
|
73
|
+
/**
|
|
74
|
+
* https://www.sanity.io/docs/api-versioning
|
|
75
|
+
* @defaultValue '2022-11-27'
|
|
76
|
+
*/
|
|
77
|
+
apiVersion?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
declare interface LanguageFilterConfigProcessed extends LanguageFilterConfig {
|
|
81
|
+
supportedLanguages: Language[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export declare interface LanguageFilterOptions {
|
|
85
|
+
languageFilter?: boolean
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export declare interface LanguageFilterSchema extends ObjectSchemaType {
|
|
89
|
+
options?: LanguageFilterOptions
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
declare interface LanguageFilterStudioContextProcessed {
|
|
93
|
+
options: Required<LanguageFilterConfigProcessed>
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
declare interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {
|
|
97
|
+
selectedLanguageIds: string[]
|
|
98
|
+
setSelectedLanguageIds: (ids: string[]) => void
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Retrieves plugin options and the currently selected
|
|
103
|
+
* language IDs from anywhere in the Studio
|
|
104
|
+
*/
|
|
105
|
+
export declare function useLanguageFilterStudioContext(): LanguageFilterStudioContextValue
|
|
106
|
+
|
|
107
|
+
export {}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
|
|
3
|
-
import {FieldMember} from 'sanity'
|
|
4
|
-
import {FieldsetState} from 'sanity'
|
|
5
|
-
import {ObjectSchemaType} from 'sanity'
|
|
3
|
+
import type {FieldMember} from 'sanity'
|
|
4
|
+
import type {FieldsetState} from 'sanity'
|
|
5
|
+
import type {ObjectSchemaType} from 'sanity'
|
|
6
6
|
import {Plugin as Plugin_2} from 'sanity'
|
|
7
|
-
import {SanityClient} from 'sanity'
|
|
7
|
+
import type {SanityClient} from 'sanity'
|
|
8
8
|
import type {SchemaType} from 'sanity'
|
|
9
9
|
|
|
10
10
|
export declare const defaultFilterField: FilterFieldFunction
|
|
@@ -12,12 +12,12 @@ export declare const defaultFilterField: FilterFieldFunction
|
|
|
12
12
|
export declare type FilterFieldFunction = (
|
|
13
13
|
enclosingType: ObjectSchemaType,
|
|
14
14
|
field: FieldMember | FieldsetState,
|
|
15
|
-
selectedLanguageIds: string[]
|
|
15
|
+
selectedLanguageIds: string[],
|
|
16
16
|
) => boolean
|
|
17
17
|
|
|
18
18
|
export declare function isLanguageFilterEnabled(
|
|
19
19
|
schemaType: SchemaType | undefined,
|
|
20
|
-
options: LanguageFilterConfig
|
|
20
|
+
options: LanguageFilterConfig,
|
|
21
21
|
): boolean
|
|
22
22
|
|
|
23
23
|
export declare type Language = {
|
|
@@ -27,7 +27,7 @@ export declare type Language = {
|
|
|
27
27
|
|
|
28
28
|
declare type LanguageCallback = (
|
|
29
29
|
client: SanityClient,
|
|
30
|
-
selectedValue: Record<string, unknown
|
|
30
|
+
selectedValue: Record<string, unknown>,
|
|
31
31
|
) => Promise<Language[]>
|
|
32
32
|
|
|
33
33
|
/**
|
package/lib/index.esm.js
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import{useState as l,createContext as r,useEffect as i,useMemo as a,useContext as o,useCallback as s}from"react";import{useClient as u,TextWithTone as d,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 v}from"@sanity/icons";import{Box as S,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){var n,l;const r=function(e){return"object"===(null==e?void 0:e.jsonType)&&"document"===z(e).name}(e)&&(null==(n=null==e?void 0:e.options)?void 0:n.languageFilter),i=!t.documentTypes;return!!(i&&!1!==r||!i&&r||e&&null!=(l=t.documentTypes)&&l.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 l=t,n=n.filter((e=>l.includes(e))),n;var l}function V({supportedLanguages:e,defaultLanguages:t}){return Array.isArray(e)?e.filter((e=>!(null!=t&&t.includes(e.id)))):[]}const $={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:j},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},_=r($);function E(t){const n=u({apiVersion:"2023-01-01"}),[r,o]=l(Array.isArray(t.options.supportedLanguages)?t.options.supportedLanguages:[]);i((()=>{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=a((()=>({...$.options,...t.options,supportedLanguages:r})),[t.options,r]),[d,c]=function(e){return l((()=>{var t;return[...null!=(t=e.defaultLanguages)?t:[],...J(e)]}))}(s);return e(_.Provider,{value:{options:s,selectedLanguageIds:d,setSelectedLanguageIds:c},children:t.renderDefault(t)})}function H(){return o(_)}const P=e=>Array.from(new Set(e));function W(){const{selectedLanguageIds:e,setSelectedLanguageIds:t,options:n}=H(),{defaultLanguages:l=[]}=n,r=a((()=>V(n)),[n]),i=s((e=>{var n;t(P([...l,...e])),n=P([...l,...e]),window.localStorage.setItem(O,JSON.stringify(n))}),[l,t]),o=s((()=>i(r.map((e=>e.id)))),[i,r]),u=s((()=>{i(l)}),[l,i]),d=s((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):P([...n,t]),i(n)}),[i,e]);return{activeLanguages:a((()=>P([...null!=l?l:[],...e])),[l,e]),allSelected:e.length===r.length+l.length,selectAll:o,selectNone:u,toggleLanguage:d}}const q=N(S)`
|
|
2
|
+
max-height: calc(100vh - 200px);
|
|
3
|
+
`;function B(){const{options:r}=H(),i=r.supportedLanguages.filter((e=>{var t;return null==(t=r.defaultLanguages)?void 0:t.includes(e.id)})),a=r.supportedLanguages.filter((e=>{var t;return!(null!=(t=r.defaultLanguages)&&t.includes(e.id))})),[o,u]=l(!1),{activeLanguages:c,allSelected:g,selectAll:p,selectNone:f,toggleLanguage:y}=W(),[v,F]=l(null),[N,j]=l(null),D=s((e=>{"ALL"===e.currentTarget.value?p():f()}),[p,f]),z=s((()=>u((e=>!e))),[]),O=s((()=>u(!1)),[]);T(O,[v,N]);const J=r.supportedLanguages.length,[V,$]=l(""),_=s((e=>{e.currentTarget.value?$(e.currentTarget.value):$("")}),[]),E=J>4,P=e(q,{overflow:"auto",children:t(b,{padding:1,space:1,children:[i.length>0&&t(n,{children:[i.map((t=>e(G,{id:t.id,title:t.title,selected:!0},t.id))),e(w,{borderTop:!0})]}),e(A,{mode:"bleed",onClick:D,justify:"flex-start",value:g?"NONE":"ALL",disabled:!!V,children:t(x,{gap:3,align:"center",children:[e(I,{size:2,children:g?e(d,{tone:"primary",children:e(m,{})}):e(h,{})}),e(S,{flex:1,children:e(I,{children:g?"Hide all":"Show all"})})]})}),E?e(k,{onChange:_,value:V,placeholder:"Filter languages"}):e(w,{borderTop:!0}),a.filter((e=>!V||e.title.toLowerCase().includes(V.toLowerCase()))).map((t=>e(G,{id:t.id,onToggle:y,selected:c.includes(t.id),title:t.title},t.id)))]})}),B=c.length===J?"Showing all":`Showing ${c.length} / ${J}`;return e(C,{content:P,open:o,portal:!0,ref:j,children:e(A,{text:B,icon:L,mode:"bleed",onClick:z,ref:F,selected:o})})}function G(n){const{id:l,onToggle:r,selected:i,title:a}=n,o=s((()=>{r&&r(l)}),[l,r]),u=!r;return e(A,{mode:"bleed",onClick:o,justify:"flex-start",disabled:u,children:t(x,{gap:3,align:"center",children:[e(I,{size:2,children:i?e(d,{tone:u?"default":"positive",children:e(y,{})}):e(v,{})}),e(S,{flex:1,children:e(I,{children:a})}),e(F,{children:l})]})})}function K(e){const{members:t,schemaType:n,renderDefault:l,...r}=e,{selectedLanguageIds:i,options:o}=H(),{filterField:s}=o;return l({...r,members:a((()=>t.filter((e=>"field"===e.kind&&s(n,e,i)||"fieldSet"===e.kind)).map((e=>"fieldSet"===e.kind?{...e,fieldSet:{...e.fieldSet,members:e.fieldSet.members.filter((e=>"field"===e.kind&&s(n,e,i)))}}:e))),[n,t,s,i]),schemaType:n,renderDefault:l})}const M=p((t=>{const n=()=>e(B,{}),l={...$.options,...t};return{name:"@sanity/language-filter",studio:{components:{layout:e=>E({...e,options:l})}},document:{unstable_languageFilter:(e,{schemaType:l,schema:r})=>D(r.get(l),t)?[...e,n]:e},form:{components:{input:t=>"root"!==t.id&&f(t.schemaType)?function(t){const{options:n}=H(),l=c(["_type"]);return D(g().get(l),n)?e(K,{...t}):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));export{j as defaultFilterField,D as isLanguageFilterEnabled,M as languageFilter,H as useLanguageFilterStudioContext};//# sourceMappingURL=index.esm.js.map
|
package/lib/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
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 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 )\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","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","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","portal","ref","text","icon","TranslateIcon","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","restProps","member","kind","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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"}
|
package/lib/index.js
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
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"),l=require("@sanity/ui"),a=require("styled-components");const r=(e,t,n)=>!e.name.startsWith("locale")||n.includes(t.name);function i(e,t){var n,s;const l=function(e){return"object"===(null==e?void 0:e.jsonType)&&"document"===o(e).name}(e)&&(null==(n=null==e?void 0:e.options)?void 0:n.languageFilter),a=!t.documentTypes;return!!(a&&!1!==l||!a&&l||e&&null!=(s=t.documentTypes)&&s.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=>!(null!=t&&t.includes(e.id)))):[]}const g={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:r},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},p=t.createContext(g);function f(s){const l=n.useClient({apiVersion:"2023-01-01"}),[a,r]=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(l,{}),r(e)}(s.options.supportedLanguages)}),[l,s.options.supportedLanguages]);const i=t.useMemo((()=>({...g.options,...s.options,supportedLanguages:a})),[s.options,a]),[o,u]=function(e){return t.useState((()=>{var t;return[...null!=(t=e.defaultLanguages)?t:[],...c(e)]}))}(i);return e.jsx(p.Provider,{value:{options:i,selectedLanguageIds:o,setSelectedLanguageIds:u},children:s.renderDefault(s)})}function x(){return t.useContext(p)}const m=e=>Array.from(new Set(e));function h(){const{selectedLanguageIds:e,setSelectedLanguageIds:n,options:s}=x(),{defaultLanguages:l=[]}=s,a=t.useMemo((()=>d(s)),[s]),r=t.useCallback((e=>{var t;n(m([...l,...e])),t=m([...l,...e]),window.localStorage.setItem(u,JSON.stringify(t))}),[l,n]),i=t.useCallback((()=>r(a.map((e=>e.id)))),[r,a]),o=t.useCallback((()=>{r(l)}),[l,r]),c=t.useCallback((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):m([...n,t]),r(n)}),[r,e]);return{activeLanguages:t.useMemo((()=>m([...null!=l?l:[],...e])),[l,e]),allSelected:e.length===a.length+l.length,selectAll:i,selectNone:o,toggleLanguage:c}}const j=a.styled(l.Box)`
|
|
2
|
+
max-height: calc(100vh - 200px);
|
|
3
|
+
`;function L(){const{options:a}=x(),r=a.supportedLanguages.filter((e=>{var t;return null==(t=a.defaultLanguages)?void 0:t.includes(e.id)})),i=a.supportedLanguages.filter((e=>{var t;return!(null!=(t=a.defaultLanguages)&&t.includes(e.id))})),[o,u]=t.useState(!1),{activeLanguages:c,allSelected:d,selectAll:g,selectNone:p,toggleLanguage:f}=h(),[m,L]=t.useState(null),[S,C]=t.useState(null),T=t.useCallback((e=>{"ALL"===e.currentTarget.value?g():p()}),[g,p]),b=t.useCallback((()=>u((e=>!e))),[]),v=t.useCallback((()=>u(!1)),[]);l.useClickOutside(v,[m,S]);const k=a.supportedLanguages.length,[F,I]=t.useState(""),w=t.useCallback((e=>{e.currentTarget.value?I(e.currentTarget.value):I("")}),[]),A=k>4,B=e.jsx(j,{overflow:"auto",children:e.jsxs(l.Stack,{padding:1,space:1,children:[r.length>0&&e.jsxs(e.Fragment,{children:[r.map((t=>e.jsx(y,{id:t.id,title:t.title,selected:!0},t.id))),e.jsx(l.Card,{borderTop:!0})]}),e.jsx(l.Button,{mode:"bleed",onClick:T,justify:"flex-start",value:d?"NONE":"ALL",disabled:!!F,children:e.jsxs(l.Flex,{gap:3,align:"center",children:[e.jsx(l.Text,{size:2,children:d?e.jsx(n.TextWithTone,{tone:"primary",children:e.jsx(s.EyeClosedIcon,{})}):e.jsx(s.EyeOpenIcon,{})}),e.jsx(l.Box,{flex:1,children:e.jsx(l.Text,{children:d?"Hide all":"Show all"})})]})}),A?e.jsx(l.TextInput,{onChange:w,value:F,placeholder:"Filter languages"}):e.jsx(l.Card,{borderTop:!0}),i.filter((e=>!F||e.title.toLowerCase().includes(F.toLowerCase()))).map((t=>e.jsx(y,{id:t.id,onToggle:f,selected:c.includes(t.id),title:t.title},t.id)))]})}),O=c.length===k?"Showing all":`Showing ${c.length} / ${k}`;return e.jsx(l.Popover,{content:B,open:o,portal:!0,ref:C,children:e.jsx(l.Button,{text:O,icon:s.TranslateIcon,mode:"bleed",onClick:b,ref:L,selected:o})})}function y(a){const{id:r,onToggle:i,selected:o,title:u}=a,c=t.useCallback((()=>{i&&i(r)}),[r,i]),d=!i;return e.jsx(l.Button,{mode:"bleed",onClick:c,justify:"flex-start",disabled:d,children:e.jsxs(l.Flex,{gap:3,align:"center",children:[e.jsx(l.Text,{size:2,children:o?e.jsx(n.TextWithTone,{tone:d?"default":"positive",children:e.jsx(s.CheckmarkCircleIcon,{})}):e.jsx(s.CircleIcon,{})}),e.jsx(l.Box,{flex:1,children:e.jsx(l.Text,{children:u})}),e.jsx(l.Badge,{children:r})]})})}function S(e){const{members:n,schemaType:s,renderDefault:l,...a}=e,{selectedLanguageIds:r,options:i}=x(),{filterField:o}=i;return l({...a,members:t.useMemo((()=>n.filter((e=>"field"===e.kind&&o(s,e,r)||"fieldSet"===e.kind)).map((e=>"fieldSet"===e.kind?{...e,fieldSet:{...e.fieldSet,members:e.fieldSet.members.filter((e=>"field"===e.kind&&o(s,e,r)))}}:e))),[s,n,o,r]),schemaType:s,renderDefault:l})}const C=n.definePlugin((t=>{const s=()=>e.jsx(L,{}),l={...g.options,...t};return{name:"@sanity/language-filter",studio:{components:{layout:e=>f({...e,options:l})}},document:{unstable_languageFilter:(e,{schemaType:n,schema:l})=>i(l.get(n),t)?[...e,s]:e},form:{components:{input:t=>"root"!==t.id&&n.isObjectSchemaType(t.schemaType)?function(t){const{options:s}=x(),l=n.useFormValue(["_type"]);return i(n.useSchema().get(l),s)?e.jsx(S,{...t}):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));exports.defaultFilterField=r,exports.isLanguageFilterEnabled=i,exports.languageFilter=C,exports.useLanguageFilterStudioContext=x;//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
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 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 )\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","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","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","portal","ref","text","icon","TranslateIcon","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","restProps","member","kind","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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"}
|
package/lib/index.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import{useState as l,createContext as r,useEffect as i,useMemo as a,useContext as o,useCallback as s}from"react";import{useClient as u,TextWithTone as d,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 v}from"@sanity/icons";import{Box as S,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){var n,l;const r=function(e){return"object"===(null==e?void 0:e.jsonType)&&"document"===z(e).name}(e)&&(null==(n=null==e?void 0:e.options)?void 0:n.languageFilter),i=!t.documentTypes;return!!(i&&!1!==r||!i&&r||e&&null!=(l=t.documentTypes)&&l.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 l=t,n=n.filter((e=>l.includes(e))),n;var l}function V({supportedLanguages:e,defaultLanguages:t}){return Array.isArray(e)?e.filter((e=>!(null!=t&&t.includes(e.id)))):[]}const $={options:{apiVersion:"2022-11-27",supportedLanguages:[],defaultLanguages:[],documentTypes:[],filterField:j},selectedLanguageIds:[],setSelectedLanguageIds:()=>console.error("LanguageFilterStudioContext not initialized")},_=r($);function E(t){const n=u({apiVersion:"2023-01-01"}),[r,o]=l(Array.isArray(t.options.supportedLanguages)?t.options.supportedLanguages:[]);i((()=>{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=a((()=>({...$.options,...t.options,supportedLanguages:r})),[t.options,r]),[d,c]=function(e){return l((()=>{var t;return[...null!=(t=e.defaultLanguages)?t:[],...J(e)]}))}(s);return e(_.Provider,{value:{options:s,selectedLanguageIds:d,setSelectedLanguageIds:c},children:t.renderDefault(t)})}function H(){return o(_)}const P=e=>Array.from(new Set(e));function W(){const{selectedLanguageIds:e,setSelectedLanguageIds:t,options:n}=H(),{defaultLanguages:l=[]}=n,r=a((()=>V(n)),[n]),i=s((e=>{var n;t(P([...l,...e])),n=P([...l,...e]),window.localStorage.setItem(O,JSON.stringify(n))}),[l,t]),o=s((()=>i(r.map((e=>e.id)))),[i,r]),u=s((()=>{i(l)}),[l,i]),d=s((t=>{let n=e;n=n.includes(t)?n.filter((e=>e!==t)):P([...n,t]),i(n)}),[i,e]);return{activeLanguages:a((()=>P([...null!=l?l:[],...e])),[l,e]),allSelected:e.length===r.length+l.length,selectAll:o,selectNone:u,toggleLanguage:d}}const q=N(S)`
|
|
2
|
+
max-height: calc(100vh - 200px);
|
|
3
|
+
`;function B(){const{options:r}=H(),i=r.supportedLanguages.filter((e=>{var t;return null==(t=r.defaultLanguages)?void 0:t.includes(e.id)})),a=r.supportedLanguages.filter((e=>{var t;return!(null!=(t=r.defaultLanguages)&&t.includes(e.id))})),[o,u]=l(!1),{activeLanguages:c,allSelected:g,selectAll:p,selectNone:f,toggleLanguage:y}=W(),[v,F]=l(null),[N,j]=l(null),D=s((e=>{"ALL"===e.currentTarget.value?p():f()}),[p,f]),z=s((()=>u((e=>!e))),[]),O=s((()=>u(!1)),[]);T(O,[v,N]);const J=r.supportedLanguages.length,[V,$]=l(""),_=s((e=>{e.currentTarget.value?$(e.currentTarget.value):$("")}),[]),E=J>4,P=e(q,{overflow:"auto",children:t(b,{padding:1,space:1,children:[i.length>0&&t(n,{children:[i.map((t=>e(G,{id:t.id,title:t.title,selected:!0},t.id))),e(w,{borderTop:!0})]}),e(A,{mode:"bleed",onClick:D,justify:"flex-start",value:g?"NONE":"ALL",disabled:!!V,children:t(x,{gap:3,align:"center",children:[e(I,{size:2,children:g?e(d,{tone:"primary",children:e(m,{})}):e(h,{})}),e(S,{flex:1,children:e(I,{children:g?"Hide all":"Show all"})})]})}),E?e(k,{onChange:_,value:V,placeholder:"Filter languages"}):e(w,{borderTop:!0}),a.filter((e=>!V||e.title.toLowerCase().includes(V.toLowerCase()))).map((t=>e(G,{id:t.id,onToggle:y,selected:c.includes(t.id),title:t.title},t.id)))]})}),B=c.length===J?"Showing all":`Showing ${c.length} / ${J}`;return e(C,{content:P,open:o,portal:!0,ref:j,children:e(A,{text:B,icon:L,mode:"bleed",onClick:z,ref:F,selected:o})})}function G(n){const{id:l,onToggle:r,selected:i,title:a}=n,o=s((()=>{r&&r(l)}),[l,r]),u=!r;return e(A,{mode:"bleed",onClick:o,justify:"flex-start",disabled:u,children:t(x,{gap:3,align:"center",children:[e(I,{size:2,children:i?e(d,{tone:u?"default":"positive",children:e(y,{})}):e(v,{})}),e(S,{flex:1,children:e(I,{children:a})}),e(F,{children:l})]})})}function K(e){const{members:t,schemaType:n,renderDefault:l,...r}=e,{selectedLanguageIds:i,options:o}=H(),{filterField:s}=o;return l({...r,members:a((()=>t.filter((e=>"field"===e.kind&&s(n,e,i)||"fieldSet"===e.kind)).map((e=>"fieldSet"===e.kind?{...e,fieldSet:{...e.fieldSet,members:e.fieldSet.members.filter((e=>"field"===e.kind&&s(n,e,i)))}}:e))),[n,t,s,i]),schemaType:n,renderDefault:l})}const M=p((t=>{const n=()=>e(B,{}),l={...$.options,...t};return{name:"@sanity/language-filter",studio:{components:{layout:e=>E({...e,options:l})}},document:{unstable_languageFilter:(e,{schemaType:l,schema:r})=>D(r.get(l),t)?[...e,n]:e},form:{components:{input:t=>"root"!==t.id&&f(t.schemaType)?function(t){const{options:n}=H(),l=c(["_type"]);return D(g().get(l),n)?e(K,{...t}):t.renderDefault(t)}(t):t.renderDefault(t)}}}}));export{j as defaultFilterField,D as isLanguageFilterEnabled,M as languageFilter,H as useLanguageFilterStudioContext};//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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 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 )\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","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","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","portal","ref","text","icon","TranslateIcon","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","restProps","member","kind","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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/language-filter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.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": {
|
|
@@ -12,20 +12,20 @@
|
|
|
12
12
|
},
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "Sanity.io <hello@sanity.io>",
|
|
15
|
+
"sideEffects": false,
|
|
15
16
|
"exports": {
|
|
16
17
|
".": {
|
|
17
|
-
"types": "./lib/src/index.d.ts",
|
|
18
18
|
"source": "./src/index.ts",
|
|
19
|
-
"import": "./lib/index.
|
|
19
|
+
"import": "./lib/index.mjs",
|
|
20
20
|
"require": "./lib/index.js",
|
|
21
|
-
"default": "./lib/index.
|
|
21
|
+
"default": "./lib/index.js"
|
|
22
22
|
},
|
|
23
23
|
"./package.json": "./package.json"
|
|
24
24
|
},
|
|
25
25
|
"main": "./lib/index.js",
|
|
26
26
|
"module": "./lib/index.esm.js",
|
|
27
27
|
"source": "./src/index.ts",
|
|
28
|
-
"types": "./lib/
|
|
28
|
+
"types": "./lib/index.d.ts",
|
|
29
29
|
"files": [
|
|
30
30
|
"src",
|
|
31
31
|
"lib",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"sanity.json"
|
|
34
34
|
],
|
|
35
35
|
"scripts": {
|
|
36
|
-
"prebuild": "npm run clean
|
|
37
|
-
"build": "pkg
|
|
36
|
+
"prebuild": "npm run clean",
|
|
37
|
+
"build": "pkg build --strict && pkg check --strict",
|
|
38
38
|
"clean": "rimraf lib",
|
|
39
39
|
"link-watch": "plugin-kit link-watch",
|
|
40
40
|
"lint": "eslint .",
|
|
@@ -44,52 +44,57 @@
|
|
|
44
44
|
"watch": "pkg-utils watch"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@sanity/icons": "^2.
|
|
47
|
+
"@sanity/icons": "^2.11.7",
|
|
48
48
|
"@sanity/incompatible-plugin": "^1.0.4",
|
|
49
|
-
"@sanity/ui": "^1.0
|
|
50
|
-
"
|
|
49
|
+
"@sanity/ui": "^2.1.0",
|
|
50
|
+
"lodash": "^4.17.21"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@
|
|
54
|
-
"@babel/preset-
|
|
55
|
-
"@
|
|
56
|
-
"@commitlint/
|
|
57
|
-
"@
|
|
58
|
-
"@sanity/
|
|
59
|
-
"@sanity/
|
|
60
|
-
"@
|
|
61
|
-
"@types/
|
|
62
|
-
"@
|
|
63
|
-
"@typescript-eslint/
|
|
64
|
-
"eslint": "^
|
|
65
|
-
"eslint
|
|
66
|
-
"eslint-config-
|
|
67
|
-
"eslint-
|
|
68
|
-
"eslint-plugin-
|
|
53
|
+
"@sanity/util": "^3.36.4",
|
|
54
|
+
"@babel/preset-env": "^7.24.4",
|
|
55
|
+
"@babel/preset-react": "^7.24.1",
|
|
56
|
+
"@commitlint/cli": "^19.2.1",
|
|
57
|
+
"@commitlint/config-conventional": "^19.1.0",
|
|
58
|
+
"@sanity/pkg-utils": "^6.1.0",
|
|
59
|
+
"@sanity/plugin-kit": "^3.1.10",
|
|
60
|
+
"@sanity/semantic-release-preset": "^4.1.7",
|
|
61
|
+
"@types/jest": "^29.5.12",
|
|
62
|
+
"@types/lodash": "^4.17.0",
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^7.6.0",
|
|
64
|
+
"@typescript-eslint/parser": "^7.6.0",
|
|
65
|
+
"eslint": "^8.57.0",
|
|
66
|
+
"eslint-config-prettier": "^9.1.0",
|
|
67
|
+
"eslint-config-sanity": "^7.1.2",
|
|
68
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
69
|
+
"eslint-plugin-react": "^7.34.1",
|
|
69
70
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
70
71
|
"husky": "^8.0.1",
|
|
71
|
-
"jest": "^29.
|
|
72
|
-
"lint-staged": "^
|
|
73
|
-
"prettier": "^2.
|
|
74
|
-
"prettier-plugin-packagejson": "^2.
|
|
75
|
-
"react": "^18",
|
|
76
|
-
"rimraf": "^
|
|
77
|
-
"sanity": "^3.
|
|
78
|
-
"styled-components": "^
|
|
79
|
-
"ts-jest": "^29.
|
|
80
|
-
"typescript": "^4.
|
|
72
|
+
"jest": "^29.7.0",
|
|
73
|
+
"lint-staged": "^15.2.2",
|
|
74
|
+
"prettier": "^3.2.5",
|
|
75
|
+
"prettier-plugin-packagejson": "^2.4.14",
|
|
76
|
+
"react": "^18.2.0",
|
|
77
|
+
"rimraf": "^4.4.1",
|
|
78
|
+
"sanity": "^3.36.4",
|
|
79
|
+
"styled-components": "^6.1.8",
|
|
80
|
+
"ts-jest": "^29.1.2",
|
|
81
|
+
"typescript": "^5.4.4"
|
|
81
82
|
},
|
|
82
83
|
"peerDependencies": {
|
|
84
|
+
"@sanity/util": "^3.36.4",
|
|
85
|
+
"@sanity/ui": "^2.1.0",
|
|
83
86
|
"react": "^18",
|
|
84
|
-
"
|
|
85
|
-
"
|
|
87
|
+
"react-dom": "^18",
|
|
88
|
+
"sanity": "^3.36.4",
|
|
89
|
+
"styled-components": "^6.1"
|
|
86
90
|
},
|
|
87
91
|
"engines": {
|
|
88
92
|
"node": ">=14"
|
|
89
93
|
},
|
|
90
94
|
"sanityPlugin": {
|
|
91
95
|
"verifyPackage": {
|
|
92
|
-
"babelConfig": false
|
|
96
|
+
"babelConfig": false,
|
|
97
|
+
"tsconfig": false
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
CheckmarkCircleIcon,
|
|
3
|
+
CircleIcon,
|
|
4
|
+
EyeClosedIcon,
|
|
5
|
+
EyeOpenIcon,
|
|
6
|
+
TranslateIcon,
|
|
7
|
+
} from '@sanity/icons'
|
|
8
|
+
import {
|
|
3
9
|
Badge,
|
|
4
10
|
Box,
|
|
5
11
|
Button,
|
|
@@ -8,20 +14,15 @@ import {
|
|
|
8
14
|
Popover,
|
|
9
15
|
Stack,
|
|
10
16
|
Text,
|
|
17
|
+
TextInput,
|
|
11
18
|
useClickOutside,
|
|
12
19
|
} from '@sanity/ui'
|
|
13
|
-
import
|
|
14
|
-
import styled from 'styled-components'
|
|
15
|
-
import {usePaneLanguages} from './usePaneLanguages'
|
|
16
|
-
import {
|
|
17
|
-
CheckmarkCircleIcon,
|
|
18
|
-
CircleIcon,
|
|
19
|
-
EyeClosedIcon,
|
|
20
|
-
EyeOpenIcon,
|
|
21
|
-
TranslateIcon,
|
|
22
|
-
} from '@sanity/icons'
|
|
20
|
+
import {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'
|
|
23
21
|
import {TextWithTone} from 'sanity'
|
|
22
|
+
import {styled} from 'styled-components'
|
|
23
|
+
|
|
24
24
|
import {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'
|
|
25
|
+
import {usePaneLanguages} from './usePaneLanguages'
|
|
25
26
|
|
|
26
27
|
const StyledBox = styled(Box)`
|
|
27
28
|
max-height: calc(100vh - 200px);
|
|
@@ -31,11 +32,11 @@ export function LanguageFilterMenuButton() {
|
|
|
31
32
|
const {options} = useLanguageFilterStudioContext()
|
|
32
33
|
|
|
33
34
|
const defaultLanguages = options.supportedLanguages.filter((l) =>
|
|
34
|
-
options.defaultLanguages?.includes(l.id)
|
|
35
|
+
options.defaultLanguages?.includes(l.id),
|
|
35
36
|
)
|
|
36
37
|
|
|
37
38
|
const languageOptions = options.supportedLanguages.filter(
|
|
38
|
-
(l) => !options.defaultLanguages?.includes(l.id)
|
|
39
|
+
(l) => !options.defaultLanguages?.includes(l.id),
|
|
39
40
|
)
|
|
40
41
|
const [open, setOpen] = useState(false)
|
|
41
42
|
const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()
|
|
@@ -52,7 +53,7 @@ export function LanguageFilterMenuButton() {
|
|
|
52
53
|
selectNone()
|
|
53
54
|
}
|
|
54
55
|
},
|
|
55
|
-
[selectAll, selectNone]
|
|
56
|
+
[selectAll, selectNone],
|
|
56
57
|
)
|
|
57
58
|
|
|
58
59
|
const handleClick = useCallback(() => setOpen((o) => !o), [])
|
|
@@ -64,12 +65,12 @@ export function LanguageFilterMenuButton() {
|
|
|
64
65
|
const langCount = options.supportedLanguages.length
|
|
65
66
|
|
|
66
67
|
// Search filter query
|
|
67
|
-
const [query, setQuery] = useState(
|
|
68
|
+
const [query, setQuery] = useState('')
|
|
68
69
|
const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {
|
|
69
70
|
if (event.currentTarget.value) {
|
|
70
71
|
setQuery(event.currentTarget.value)
|
|
71
72
|
} else {
|
|
72
|
-
setQuery(
|
|
73
|
+
setQuery('')
|
|
73
74
|
}
|
|
74
75
|
}, [])
|
|
75
76
|
|
|
@@ -105,7 +106,7 @@ export function LanguageFilterMenuButton() {
|
|
|
105
106
|
)}
|
|
106
107
|
</Text>
|
|
107
108
|
<Box flex={1}>
|
|
108
|
-
<Text>{allSelected ?
|
|
109
|
+
<Text>{allSelected ? 'Hide all' : 'Show all'}</Text>
|
|
109
110
|
</Box>
|
|
110
111
|
</Flex>
|
|
111
112
|
</Button>
|
|
@@ -138,7 +139,7 @@ export function LanguageFilterMenuButton() {
|
|
|
138
139
|
|
|
139
140
|
const buttonText =
|
|
140
141
|
activeLanguages.length === langCount
|
|
141
|
-
?
|
|
142
|
+
? 'Showing all'
|
|
142
143
|
: `Showing ${activeLanguages.length} / ${langCount}`
|
|
143
144
|
return (
|
|
144
145
|
<Popover content={content} open={open} portal ref={setPopover}>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {ObjectInputProps, ObjectMember, useFormValue, useSchema} from 'sanity'
|
|
3
|
-
|
|
1
|
+
import {useMemo} from 'react'
|
|
2
|
+
import {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'
|
|
3
|
+
|
|
4
4
|
import {isLanguageFilterEnabled} from './filterField'
|
|
5
|
+
import {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'
|
|
5
6
|
|
|
6
7
|
// First check that this Object is in a schema type for which language-filter is enabled
|
|
7
8
|
export function FilteredObjectWrapper(props: ObjectInputProps) {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import {createContext, useContext, useEffect, useMemo, useState} from 'react'
|
|
2
|
+
import {type LayoutProps, useClient} from 'sanity'
|
|
3
|
+
|
|
4
|
+
import {defaultFilterField} from './filterField'
|
|
5
|
+
import type {
|
|
3
6
|
Language,
|
|
4
7
|
LanguageCallback,
|
|
5
8
|
LanguageFilterConfig,
|
|
6
9
|
LanguageFilterConfigProcessed,
|
|
7
10
|
} from './types'
|
|
8
|
-
import {LayoutProps, useClient} from 'sanity'
|
|
9
|
-
import {defaultFilterField} from './filterField'
|
|
10
11
|
import {useSelectedLanguageIds} from './useSelectedLanguageIds'
|
|
11
12
|
|
|
12
13
|
export interface LanguageFilterStudioContextProps {
|
|
@@ -44,11 +45,11 @@ const LanguageFilterStudioContext =
|
|
|
44
45
|
* and provide them to a Studio-wide context
|
|
45
46
|
*/
|
|
46
47
|
export function LanguageFilterStudioProvider(
|
|
47
|
-
props: LayoutProps & LanguageFilterStudioContextProps
|
|
48
|
+
props: LayoutProps & LanguageFilterStudioContextProps,
|
|
48
49
|
) {
|
|
49
50
|
const client = useClient({apiVersion: '2023-01-01'})
|
|
50
51
|
const [languages, setLanguages] = useState<Language[]>(
|
|
51
|
-
Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : []
|
|
52
|
+
Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],
|
|
52
53
|
)
|
|
53
54
|
useEffect(() => {
|
|
54
55
|
let asyncLanguages: Language[] = []
|
package/src/filterField.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type {FieldMember, ObjectSchemaType} from 'sanity'
|
|
2
|
+
|
|
1
3
|
import {defaultFilterField, isLanguageFilterEnabled} from './filterField'
|
|
2
|
-
import {FieldMember, ObjectSchemaType} from 'sanity'
|
|
3
4
|
|
|
4
5
|
describe('filterField', () => {
|
|
5
6
|
describe('isLanguageFilterEnabled', () => {
|
|
@@ -25,7 +26,7 @@ describe('filterField', () => {
|
|
|
25
26
|
it('should be disabled when documentTypes is missing and options.languageFilter: false', () => {
|
|
26
27
|
const enabled = isLanguageFilterEnabled(
|
|
27
28
|
{...docType, options: {languageFilter: false}},
|
|
28
|
-
{supportedLanguages: []}
|
|
29
|
+
{supportedLanguages: []},
|
|
29
30
|
)
|
|
30
31
|
expect(enabled).toBeFalsy()
|
|
31
32
|
})
|
|
@@ -33,7 +34,7 @@ describe('filterField', () => {
|
|
|
33
34
|
it('should be enabled when documentTypes is contains doc-type name', () => {
|
|
34
35
|
const enabled = isLanguageFilterEnabled(
|
|
35
36
|
{...docType, options: {languageFilter: false}},
|
|
36
|
-
{supportedLanguages: [], documentTypes: [docType.name]}
|
|
37
|
+
{supportedLanguages: [], documentTypes: [docType.name]},
|
|
37
38
|
)
|
|
38
39
|
expect(enabled).toBeTruthy()
|
|
39
40
|
})
|
|
@@ -41,7 +42,7 @@ describe('filterField', () => {
|
|
|
41
42
|
it('should be enabled when documentTypes does not contain doc-type name, but options.languageFilter: true', () => {
|
|
42
43
|
const enabled = isLanguageFilterEnabled(
|
|
43
44
|
{...docType, options: {languageFilter: true}},
|
|
44
|
-
{supportedLanguages: [], documentTypes: []}
|
|
45
|
+
{supportedLanguages: [], documentTypes: []},
|
|
45
46
|
)
|
|
46
47
|
expect(enabled).toBeTruthy()
|
|
47
48
|
})
|
|
@@ -73,6 +74,8 @@ describe('filterField', () => {
|
|
|
73
74
|
changed: false,
|
|
74
75
|
value: undefined,
|
|
75
76
|
},
|
|
77
|
+
groups: [],
|
|
78
|
+
inSelectedGroup: false,
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
it('should filter -> true for nb field inside local-prefixed object', () => {
|
|
@@ -89,7 +92,7 @@ describe('filterField', () => {
|
|
|
89
92
|
const result = defaultFilterField(
|
|
90
93
|
{...localePrefixedObject, name: 'not-start-with-locale-field'},
|
|
91
94
|
member,
|
|
92
|
-
['nb']
|
|
95
|
+
['nb'],
|
|
93
96
|
)
|
|
94
97
|
expect(result).toBeTruthy()
|
|
95
98
|
})
|
package/src/filterField.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import type {SchemaType} from 'sanity'
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'
|
|
3
4
|
|
|
4
5
|
export const defaultFilterField: FilterFieldFunction = (
|
|
5
6
|
enclosingType,
|
|
6
7
|
field,
|
|
7
|
-
selectedLanguageIds
|
|
8
|
+
selectedLanguageIds,
|
|
8
9
|
) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)
|
|
9
10
|
|
|
10
11
|
export function isLanguageFilterEnabled(
|
|
11
12
|
schemaType: SchemaType | undefined,
|
|
12
|
-
options: LanguageFilterConfig
|
|
13
|
+
options: LanguageFilterConfig,
|
|
13
14
|
): boolean {
|
|
14
15
|
const schemaFilter =
|
|
15
16
|
isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter
|
package/src/getSelectedValue.ts
CHANGED
|
@@ -6,7 +6,7 @@ export const getSelectedValue = (
|
|
|
6
6
|
| {
|
|
7
7
|
[x: string]: unknown
|
|
8
8
|
}
|
|
9
|
-
| undefined
|
|
9
|
+
| undefined,
|
|
10
10
|
): Record<string, unknown> => {
|
|
11
11
|
if (!select || !document) {
|
|
12
12
|
return {}
|
|
@@ -19,7 +19,7 @@ export const getSelectedValue = (
|
|
|
19
19
|
if (Array.isArray(value)) {
|
|
20
20
|
// If there are references in the array, ensure they have `_ref` set, otherwise they are considered empty and can safely be ignored
|
|
21
21
|
value = value.filter((item) =>
|
|
22
|
-
typeof item === 'object' ? item?._type === 'reference' && '_ref' in item : true
|
|
22
|
+
typeof item === 'object' ? item?._type === 'reference' && '_ref' in item : true,
|
|
23
23
|
)
|
|
24
24
|
}
|
|
25
25
|
selectedValue[key] = value
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Plugin function
|
|
3
3
|
*/
|
|
4
|
-
export {languageFilter} from './plugin'
|
|
5
|
-
|
|
6
4
|
export {defaultFilterField, isLanguageFilterEnabled} from './filterField'
|
|
7
|
-
|
|
5
|
+
export {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'
|
|
6
|
+
export {languageFilter} from './plugin'
|
|
8
7
|
export type {
|
|
9
|
-
LanguageFilterConfig,
|
|
10
|
-
LanguageFilterSchema,
|
|
11
|
-
LanguageFilterOptions,
|
|
12
8
|
FilterFieldFunction,
|
|
13
9
|
Language,
|
|
10
|
+
LanguageFilterConfig,
|
|
11
|
+
LanguageFilterOptions,
|
|
12
|
+
LanguageFilterSchema,
|
|
14
13
|
} from './types'
|
|
15
|
-
|
|
16
|
-
export {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'
|
package/src/plugin.tsx
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import {
|
|
3
2
|
definePlugin,
|
|
4
|
-
DocumentLanguageFilterComponent,
|
|
3
|
+
type DocumentLanguageFilterComponent,
|
|
5
4
|
isObjectSchemaType,
|
|
6
|
-
ObjectInputProps,
|
|
5
|
+
type ObjectInputProps,
|
|
7
6
|
} from 'sanity'
|
|
8
|
-
|
|
9
|
-
import {LanguageFilterMenuButton} from './LanguageFilterMenuButton'
|
|
10
|
-
import {LanguageFilterConfig} from './types'
|
|
7
|
+
|
|
11
8
|
import {isLanguageFilterEnabled} from './filterField'
|
|
9
|
+
import {LanguageFilterMenuButton} from './LanguageFilterMenuButton'
|
|
10
|
+
import {FilteredObjectWrapper} from './LanguageFilterObjectInput'
|
|
12
11
|
import {defaultContextValue, LanguageFilterStudioProvider} from './LanguageFilterStudioContext'
|
|
12
|
+
import type {LanguageFilterConfig} from './types'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* ## Usage in sanity.config.ts (or .js)
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {FieldMember, FieldsetState, ObjectSchemaType, SanityClient} from 'sanity'
|
|
1
|
+
import type {FieldMember, FieldsetState, ObjectSchemaType, SanityClient} from 'sanity'
|
|
2
2
|
|
|
3
3
|
export interface LanguageFilterOptions {
|
|
4
4
|
languageFilter?: boolean
|
|
@@ -15,13 +15,13 @@ export type Language = {
|
|
|
15
15
|
|
|
16
16
|
export type LanguageCallback = (
|
|
17
17
|
client: SanityClient,
|
|
18
|
-
selectedValue: Record<string, unknown
|
|
18
|
+
selectedValue: Record<string, unknown>,
|
|
19
19
|
) => Promise<Language[]>
|
|
20
20
|
|
|
21
21
|
export type FilterFieldFunction = (
|
|
22
22
|
enclosingType: ObjectSchemaType,
|
|
23
23
|
field: FieldMember | FieldsetState,
|
|
24
|
-
selectedLanguageIds: string[]
|
|
24
|
+
selectedLanguageIds: string[],
|
|
25
25
|
) => boolean
|
|
26
26
|
|
|
27
27
|
export interface LanguageFilterConfig {
|
package/src/usePaneLanguages.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {useCallback, useMemo} from 'react'
|
|
2
2
|
|
|
3
|
-
import {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'
|
|
4
3
|
import {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'
|
|
4
|
+
import {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'
|
|
5
5
|
|
|
6
6
|
const unique = (arr: string[]) => Array.from(new Set(arr))
|
|
7
7
|
|
|
@@ -22,12 +22,12 @@ export function usePaneLanguages(): {
|
|
|
22
22
|
setSelectedLanguageIds(unique([...defaultLanguages, ...ids]))
|
|
23
23
|
persistLanguageIds(unique([...defaultLanguages, ...ids]))
|
|
24
24
|
},
|
|
25
|
-
[defaultLanguages, setSelectedLanguageIds]
|
|
25
|
+
[defaultLanguages, setSelectedLanguageIds],
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
const selectAll = useCallback(
|
|
29
29
|
() => updateSelectedIds(selectableLanguages.map((l) => l.id)),
|
|
30
|
-
[updateSelectedIds, selectableLanguages]
|
|
30
|
+
[updateSelectedIds, selectableLanguages],
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
const selectNone = useCallback(() => {
|
|
@@ -46,12 +46,12 @@ export function usePaneLanguages(): {
|
|
|
46
46
|
|
|
47
47
|
updateSelectedIds(lang)
|
|
48
48
|
},
|
|
49
|
-
[updateSelectedIds, selectedLanguageIds]
|
|
49
|
+
[updateSelectedIds, selectedLanguageIds],
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
const activeLanguages = useMemo(
|
|
53
53
|
() => unique([...(defaultLanguages ?? []), ...selectedLanguageIds]),
|
|
54
|
-
[defaultLanguages, selectedLanguageIds]
|
|
54
|
+
[defaultLanguages, selectedLanguageIds],
|
|
55
55
|
)
|
|
56
56
|
|
|
57
57
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {Language, LanguageFilterConfig} from './types'
|
|
2
1
|
import {useState} from 'react'
|
|
2
|
+
|
|
3
|
+
import type {Language, LanguageFilterConfig} from './types'
|
|
3
4
|
const storageKey = '@sanity/plugin/language-filter/selected-languages'
|
|
4
5
|
|
|
5
6
|
export function getPersistedLanguageIds(options: LanguageFilterConfig): string[] {
|
|
@@ -36,7 +37,7 @@ export function getSelectableLanguages({
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export function useSelectedLanguageIds(
|
|
39
|
-
options: LanguageFilterConfig
|
|
40
|
+
options: LanguageFilterConfig,
|
|
40
41
|
): [string[], (ids: string[]) => void] {
|
|
41
42
|
return useState(() => [...(options.defaultLanguages ?? []), ...getPersistedLanguageIds(options)])
|
|
42
43
|
}
|