@sanity/language-filter 4.0.6 → 5.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/lib/index.mjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/filterField.ts","../src/useSelectedLanguageIds.ts","../src/LanguageFilterStudioContext.tsx","../src/usePaneLanguages.ts","../src/LanguageFilterMenuButton.tsx","../src/LanguageFilterObjectInput.tsx","../src/plugin.tsx"],"sourcesContent":["import type {SchemaType} from 'sanity'\n\nimport type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'\n\nexport const defaultFilterField: FilterFieldFunction = (\n enclosingType,\n field,\n selectedLanguageIds,\n) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)\n\nexport function isLanguageFilterEnabled(\n schemaType: SchemaType | undefined,\n options: LanguageFilterConfig,\n): boolean {\n const schemaFilter =\n isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter\n const defaultEnabled = !options.documentTypes\n\n return !!(\n (defaultEnabled && schemaFilter !== false) ||\n (!defaultEnabled && schemaFilter) ||\n (schemaType && options.documentTypes?.includes(schemaType.name))\n )\n}\n\nfunction isDocument(schemaType?: SchemaType) {\n return schemaType?.jsonType === 'object' && getRootType(schemaType).name === 'document'\n}\n\nfunction getRootType(schema: SchemaType): SchemaType {\n if (schema.type) {\n return getRootType(schema.type)\n }\n return schema\n}\n","import {useState} from 'react'\n\nimport type {Language, LanguageFilterConfig} from './types'\nconst storageKey = '@sanity/plugin/language-filter/selected-languages'\n\nexport function getPersistedLanguageIds(options: LanguageFilterConfig): string[] {\n const selectableLangs = getSelectableLanguages(options).map((l) => l.id)\n\n let selected: string[] = selectableLangs\n try {\n const persistedValue = window.localStorage.getItem(storageKey)\n if (persistedValue) {\n selected = JSON.parse(persistedValue)\n }\n } catch (err) {} // eslint-disable-line no-empty\n\n // constrain persisted/selected languages to the ones currently supported\n selected = intersection(selected, selectableLangs)\n return selected\n}\n\nexport function persistLanguageIds(languageIds: string[]): void {\n window.localStorage.setItem(storageKey, JSON.stringify(languageIds))\n}\n\nfunction intersection(array1: string[], array2: string[]) {\n return array1.filter((value) => array2.includes(value))\n}\n\nexport function getSelectableLanguages({\n supportedLanguages,\n defaultLanguages,\n}: LanguageFilterConfig): Language[] {\n return Array.isArray(supportedLanguages)\n ? supportedLanguages.filter((lang) => !defaultLanguages?.includes(lang.id))\n : []\n}\n\nexport function useSelectedLanguageIds(\n options: LanguageFilterConfig,\n): [string[], (ids: string[]) => void] {\n return useState(() => [...(options.defaultLanguages ?? []), ...getPersistedLanguageIds(options)])\n}\n","import {createContext, useContext, useEffect, useMemo, useState} from 'react'\nimport {type LayoutProps, useClient} from 'sanity'\n\nimport {defaultFilterField} from './filterField'\nimport type {\n Language,\n LanguageCallback,\n LanguageFilterConfig,\n LanguageFilterConfigProcessed,\n} from './types'\nimport {useSelectedLanguageIds} from './useSelectedLanguageIds'\n\nexport interface LanguageFilterStudioContextProps {\n // eslint-disable-next-line react/require-default-props\n options: Required<LanguageFilterConfig>\n}\n\nexport interface LanguageFilterStudioContextProcessed {\n options: Required<LanguageFilterConfigProcessed>\n}\n\nexport interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {\n selectedLanguageIds: string[]\n setSelectedLanguageIds: (ids: string[]) => void\n}\n\nexport const defaultContextValue: LanguageFilterStudioContextValue = {\n options: {\n apiVersion: '2022-11-27',\n supportedLanguages: [],\n defaultLanguages: [],\n documentTypes: [],\n filterField: defaultFilterField,\n },\n selectedLanguageIds: [],\n setSelectedLanguageIds: () => console.error('LanguageFilterStudioContext not initialized'),\n}\n\nconst LanguageFilterStudioContext =\n createContext<LanguageFilterStudioContextValue>(defaultContextValue)\n\n/**\n * This is a separate Provider from the Context that wraps the document pane\n * but it used to listen to changes to the selected language IDs inside it\n * and provide them to a Studio-wide context\n */\nexport function LanguageFilterStudioProvider(\n props: LayoutProps & LanguageFilterStudioContextProps,\n) {\n const client = useClient({apiVersion: '2023-01-01'})\n const [languages, setLanguages] = useState<Language[]>(\n Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],\n )\n useEffect(() => {\n let asyncLanguages: Language[] = []\n\n async function getLanguages(supportedLanguagesCallback: LanguageCallback) {\n asyncLanguages = await supportedLanguagesCallback(client, {})\n setLanguages(asyncLanguages)\n }\n\n if (!Array.isArray(props.options.supportedLanguages)) {\n getLanguages(props.options.supportedLanguages)\n }\n }, [client, props.options.supportedLanguages])\n\n const options = useMemo<Required<LanguageFilterConfigProcessed>>(() => {\n return {\n ...defaultContextValue.options,\n ...props.options,\n supportedLanguages: languages,\n }\n }, [props.options, languages])\n\n const [selectedLanguageIds, setSelectedLanguageIds] = useSelectedLanguageIds(options)\n\n return (\n <LanguageFilterStudioContext.Provider\n value={{options, selectedLanguageIds, setSelectedLanguageIds}}\n >\n {props.renderDefault(props)}\n </LanguageFilterStudioContext.Provider>\n )\n}\n\n/**\n * Retrieves plugin options and the currently selected\n * language IDs from anywhere in the Studio\n */\nexport function useLanguageFilterStudioContext() {\n return useContext(LanguageFilterStudioContext)\n}\n","import {useCallback, useMemo} from 'react'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {getSelectableLanguages, persistLanguageIds} from './useSelectedLanguageIds'\n\nconst unique = (arr: string[]) => Array.from(new Set(arr))\n\nexport function usePaneLanguages(): {\n activeLanguages: string[]\n allSelected: boolean\n selectAll: () => void\n selectNone: () => void\n toggleLanguage: (languageId: string) => void\n} {\n const {selectedLanguageIds, setSelectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {defaultLanguages = []} = options\n\n const selectableLanguages = useMemo(() => getSelectableLanguages(options), [options])\n\n const updateSelectedIds = useCallback(\n (ids: string[]) => {\n setSelectedLanguageIds(unique([...defaultLanguages, ...ids]))\n persistLanguageIds(unique([...defaultLanguages, ...ids]))\n },\n [defaultLanguages, setSelectedLanguageIds],\n )\n\n const selectAll = useCallback(\n () => updateSelectedIds(selectableLanguages.map((l) => l.id)),\n [updateSelectedIds, selectableLanguages],\n )\n\n const selectNone = useCallback(() => {\n updateSelectedIds(defaultLanguages)\n }, [defaultLanguages, updateSelectedIds])\n\n const toggleLanguage = useCallback(\n (languageId: string) => {\n let lang = selectedLanguageIds\n\n if (lang.includes(languageId)) {\n lang = lang.filter((l) => l !== languageId)\n } else {\n lang = unique([...lang, languageId])\n }\n\n updateSelectedIds(lang)\n },\n [updateSelectedIds, selectedLanguageIds],\n )\n\n const activeLanguages = useMemo(\n () => unique([...(defaultLanguages ?? []), ...selectedLanguageIds]),\n [defaultLanguages, selectedLanguageIds],\n )\n\n return {\n activeLanguages,\n allSelected:\n selectedLanguageIds.length === selectableLanguages.length + defaultLanguages.length,\n selectAll,\n selectNone,\n toggleLanguage,\n }\n}\n","import {\n CheckmarkCircleIcon,\n CircleIcon,\n EyeClosedIcon,\n EyeOpenIcon,\n TranslateIcon,\n} from '@sanity/icons'\nimport {\n Badge,\n Box,\n Button,\n Card,\n Flex,\n Popover,\n Stack,\n Text,\n TextInput,\n useClickOutside,\n} from '@sanity/ui'\nimport {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'\nimport {TextWithTone} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\nimport {usePaneLanguages} from './usePaneLanguages'\n\nconst StyledBox = styled(Box)`\n max-height: calc(100vh - 200px);\n`\n\nexport function LanguageFilterMenuButton() {\n const {options} = useLanguageFilterStudioContext()\n\n const defaultLanguages = options.supportedLanguages.filter((l) =>\n options.defaultLanguages?.includes(l.id),\n )\n\n const languageOptions = options.supportedLanguages.filter(\n (l) => !options.defaultLanguages?.includes(l.id),\n )\n const [open, setOpen] = useState(false)\n const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()\n const [button, setButton] = useState<HTMLElement | null>(null)\n const [popover, setPopover] = useState<HTMLElement | null>(null)\n\n const handleToggleAll: MouseEventHandler<HTMLButtonElement> = useCallback(\n (event) => {\n const checked = event.currentTarget.value === 'ALL'\n\n if (checked) {\n selectAll()\n } else {\n selectNone()\n }\n },\n [selectAll, selectNone],\n )\n\n const handleClick = useCallback(() => setOpen((o) => !o), [])\n\n const handleClickOutside = useCallback(() => setOpen(false), [])\n\n useClickOutside(handleClickOutside, [button, popover])\n\n const langCount = options.supportedLanguages.length\n\n // Search filter query\n const [query, setQuery] = useState('')\n const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {\n if (event.currentTarget.value) {\n setQuery(event.currentTarget.value)\n } else {\n setQuery('')\n }\n }, [])\n\n const showSearch = langCount > 4\n\n const content = (\n <StyledBox overflow=\"auto\">\n <Stack padding={1} space={1}>\n {defaultLanguages.length > 0 && (\n <>\n {defaultLanguages.map((l) => (\n <LanguageFilterOption key={l.id} id={l.id} title={l.title} selected />\n ))}\n <Card borderTop />\n </>\n )}\n\n <Button\n mode=\"bleed\"\n onClick={handleToggleAll}\n justify=\"flex-start\"\n value={allSelected ? 'NONE' : 'ALL'}\n disabled={!!query}\n >\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {allSelected ? (\n <TextWithTone tone=\"primary\">\n <EyeClosedIcon />\n </TextWithTone>\n ) : (\n <EyeOpenIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{allSelected ? 'Hide all' : 'Show all'}</Text>\n </Box>\n </Flex>\n </Button>\n\n {showSearch ? (\n <TextInput onChange={handleQuery} value={query} placeholder=\"Filter languages\" />\n ) : (\n <Card borderTop />\n )}\n\n {languageOptions\n .filter((language) => {\n if (query) {\n return language.title.toLowerCase().includes(query.toLowerCase())\n }\n return true\n })\n .map((lang) => (\n <LanguageFilterOption\n id={lang.id}\n key={lang.id}\n onToggle={toggleLanguage}\n selected={activeLanguages.includes(lang.id)}\n title={lang.title}\n />\n ))}\n </Stack>\n </StyledBox>\n )\n\n const buttonText =\n activeLanguages.length === langCount\n ? 'Showing all'\n : `Showing ${activeLanguages.length} / ${langCount}`\n return (\n <Popover animate content={content} open={open} portal ref={setPopover}>\n <Button\n text={buttonText}\n icon={TranslateIcon}\n mode=\"bleed\"\n onClick={handleClick}\n ref={setButton}\n selected={open}\n />\n </Popover>\n )\n}\n\nfunction LanguageFilterOption(props: {\n id: string\n selected: boolean\n title: string\n // eslint-disable-next-line react/require-default-props\n onToggle?: (id: string) => void\n}) {\n const {id, onToggle, selected, title} = props\n\n const handleChange = useCallback(() => {\n if (onToggle) {\n onToggle(id)\n }\n }, [id, onToggle])\n\n const disabled = !onToggle\n\n return (\n <Button mode=\"bleed\" onClick={handleChange} justify=\"flex-start\" disabled={disabled}>\n <Flex gap={3} align=\"center\">\n <Text size={2}>\n {selected ? (\n <TextWithTone tone={disabled ? 'default' : 'positive'}>\n <CheckmarkCircleIcon />\n </TextWithTone>\n ) : (\n <CircleIcon />\n )}\n </Text>\n <Box flex={1}>\n <Text>{title}</Text>\n </Box>\n <Badge>{id}</Badge>\n </Flex>\n </Button>\n )\n}\n","import {useMemo} from 'react'\nimport {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'\n\n// First check that this Object is in a schema type for which language-filter is enabled\nexport function FilteredObjectWrapper(props: ObjectInputProps) {\n const {options} = useLanguageFilterStudioContext()\n\n const documentType = useFormValue(['_type']) as string\n const schema = useSchema()\n const languageFilterEnabled = isLanguageFilterEnabled(schema.get(documentType), options)\n return languageFilterEnabled ? <FilteredObjectInput {...props} /> : props.renderDefault(props)\n}\n\n// Modify the object members based on selected languages in the filter\nexport function FilteredObjectInput(props: ObjectInputProps) {\n const {members: membersProp, schemaType, renderDefault, ...restProps} = props\n const {selectedLanguageIds, options} = useLanguageFilterStudioContext()\n const {filterField} = options\n\n const members: ObjectMember[] = useMemo(() => {\n return membersProp\n .filter((member) => {\n return (\n (member.kind === 'field' && filterField(schemaType, member, selectedLanguageIds)) ||\n member.kind === 'fieldSet' ||\n member.kind === 'error'\n )\n })\n .map((member) => {\n if (member.kind === 'fieldSet') {\n return {\n ...member,\n fieldSet: {\n ...member.fieldSet,\n members: member.fieldSet.members.filter((fieldsetMember) => {\n return (\n fieldsetMember.kind === 'field' &&\n filterField(schemaType, fieldsetMember, selectedLanguageIds)\n )\n }),\n },\n }\n }\n return member\n })\n }, [schemaType, membersProp, filterField, selectedLanguageIds])\n\n return renderDefault({...restProps, members, schemaType, renderDefault})\n}\n","import {\n definePlugin,\n type DocumentLanguageFilterComponent,\n isObjectSchemaType,\n type ObjectInputProps,\n} from 'sanity'\n\nimport {isLanguageFilterEnabled} from './filterField'\nimport {LanguageFilterMenuButton} from './LanguageFilterMenuButton'\nimport {FilteredObjectWrapper} from './LanguageFilterObjectInput'\nimport {defaultContextValue, LanguageFilterStudioProvider} from './LanguageFilterStudioContext'\nimport type {LanguageFilterConfig} from './types'\n\n/**\n * ## Usage in sanity.config.ts (or .js)\n *\n * ```\n * import {defineConfig} from 'sanity'\n * import {languageFilter} from '@sanity/language-filter'\n *\n * export const defineConfig({\n * /...\n * plugins: [\n * languageFilter({\n * supportedLanguages: [\n * {id: 'nb', title: 'Norwegian (Bokmål)'},\n * {id: 'nn', title: 'Norwegian (Nynorsk)'},\n * {id: 'en', title: 'English'},\n * {id: 'es', title: 'Spanish'},\n * {id: 'arb', title: 'Arabic'},\n * {id: 'pt', title: 'Portuguese'},\n * //...\n * ],\n * // Select Norwegian (Bokmål) by default\n * defaultLanguages: ['nb'],\n * // Only show language filter for document type `page` (schemaType.name)\n * // Can also enable via document-options: options.languageFilter: true\n * documentTypes: ['page'],\n * // default filter function shown\n * filterField: (enclosingType, field, selectedLanguageIds) =>\n * !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name),\n * })\n * ]\n * })\n * ```\n */\nexport const languageFilter = definePlugin<LanguageFilterConfig>((options) => {\n const RenderLanguageFilter: DocumentLanguageFilterComponent = () => {\n return <LanguageFilterMenuButton />\n }\n\n const pluginOptions = {\n ...defaultContextValue.options,\n ...options,\n }\n\n return {\n name: '@sanity/language-filter',\n studio: {\n components: {\n layout: (props) => LanguageFilterStudioProvider({...props, options: pluginOptions}),\n },\n },\n\n document: {\n unstable_languageFilter: (prev, {schemaType, schema}) => {\n if (isLanguageFilterEnabled(schema.get(schemaType), options)) {\n return [...prev, RenderLanguageFilter]\n }\n return prev\n },\n },\n\n form: {\n components: {\n input: (props) => {\n if (props.id !== 'root' && isObjectSchemaType(props.schemaType)) {\n return FilteredObjectWrapper(props as ObjectInputProps)\n }\n\n return props.renderDefault(props)\n },\n },\n },\n }\n})\n"],"names":["defaultFilterField","enclosingType","field","selectedLanguageIds","name","startsWith","includes","isLanguageFilterEnabled","schemaType","options","schemaFilter","jsonType","getRootType","isDocument","languageFilter","defaultEnabled","documentTypes","schema","type","storageKey","getPersistedLanguageIds","selectableLangs","getSelectableLanguages","map","l","id","selected","persistedValue","window","localStorage","getItem","JSON","parse","array2","filter","value","supportedLanguages","defaultLanguages","Array","isArray","lang","defaultContextValue","apiVersion","filterField","setSelectedLanguageIds","console","error","LanguageFilterStudioContext","createContext","useLanguageFilterStudioContext","useContext","unique","arr","from","Set","StyledBox","styled","Box","LanguageFilterMenuButton","languageOptions","open","setOpen","useState","activeLanguages","allSelected","selectAll","selectNone","toggleLanguage","selectableLanguages","useMemo","updateSelectedIds","useCallback","ids","languageIds","setItem","stringify","languageId","length","usePaneLanguages","button","setButton","popover","setPopover","handleToggleAll","event","currentTarget","handleClick","o","handleClickOutside","useClickOutside","langCount","query","setQuery","handleQuery","showSearch","content","jsx","overflow","children","jsxs","Stack","padding","space","Fragment","LanguageFilterOption","title","Card","borderTop","Button","mode","onClick","justify","disabled","Flex","gap","align","Text","size","TextWithTone","tone","EyeClosedIcon","EyeOpenIcon","flex","TextInput","onChange","placeholder","language","toLowerCase","onToggle","buttonText","Popover","animate","portal","ref","text","icon","TranslateIcon","props","handleChange","CheckmarkCircleIcon","CircleIcon","Badge","FilteredObjectInput","members","membersProp","renderDefault","restProps","member","kind","fieldSet","fieldsetMember","definePlugin","RenderLanguageFilter","pluginOptions","studio","components","layout","client","useClient","languages","setLanguages","useEffect","asyncLanguages","async","supportedLanguagesCallback","getLanguages","useSelectedLanguageIds","Provider","LanguageFilterStudioProvider","document","unstable_languageFilter","prev","get","form","input","isObjectSchemaType","documentType","useFormValue","useSchema","FilteredObjectWrapper"],"mappings":"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"}
package/sanity.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "parts": [
3
- {
4
- "implements": "part:@sanity/base/sanity-root",
5
- "path": "./v2-incompatible.js"
6
- }
7
- ]
8
- }
@@ -1,194 +0,0 @@
1
- import {
2
- CheckmarkCircleIcon,
3
- CircleIcon,
4
- EyeClosedIcon,
5
- EyeOpenIcon,
6
- TranslateIcon,
7
- } from '@sanity/icons'
8
- import {
9
- Badge,
10
- Box,
11
- Button,
12
- Card,
13
- Flex,
14
- Popover,
15
- Stack,
16
- Text,
17
- TextInput,
18
- useClickOutside,
19
- } from '@sanity/ui'
20
- import {type FormEvent, type MouseEventHandler, useCallback, useState} from 'react'
21
- import {TextWithTone} from 'sanity'
22
- import {styled} from 'styled-components'
23
-
24
- import {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'
25
- import {usePaneLanguages} from './usePaneLanguages'
26
-
27
- const StyledBox = styled(Box)`
28
- max-height: calc(100vh - 200px);
29
- `
30
-
31
- export function LanguageFilterMenuButton() {
32
- const {options} = useLanguageFilterStudioContext()
33
-
34
- const defaultLanguages = options.supportedLanguages.filter((l) =>
35
- options.defaultLanguages?.includes(l.id),
36
- )
37
-
38
- const languageOptions = options.supportedLanguages.filter(
39
- (l) => !options.defaultLanguages?.includes(l.id),
40
- )
41
- const [open, setOpen] = useState(false)
42
- const {activeLanguages, allSelected, selectAll, selectNone, toggleLanguage} = usePaneLanguages()
43
- const [button, setButton] = useState<HTMLElement | null>(null)
44
- const [popover, setPopover] = useState<HTMLElement | null>(null)
45
-
46
- const handleToggleAll: MouseEventHandler<HTMLButtonElement> = useCallback(
47
- (event) => {
48
- const checked = event.currentTarget.value === 'ALL'
49
-
50
- if (checked) {
51
- selectAll()
52
- } else {
53
- selectNone()
54
- }
55
- },
56
- [selectAll, selectNone],
57
- )
58
-
59
- const handleClick = useCallback(() => setOpen((o) => !o), [])
60
-
61
- const handleClickOutside = useCallback(() => setOpen(false), [])
62
-
63
- useClickOutside(handleClickOutside, [button, popover])
64
-
65
- const langCount = options.supportedLanguages.length
66
-
67
- // Search filter query
68
- const [query, setQuery] = useState('')
69
- const handleQuery = useCallback((event: FormEvent<HTMLInputElement>) => {
70
- if (event.currentTarget.value) {
71
- setQuery(event.currentTarget.value)
72
- } else {
73
- setQuery('')
74
- }
75
- }, [])
76
-
77
- const showSearch = langCount > 4
78
-
79
- const content = (
80
- <StyledBox overflow="auto">
81
- <Stack padding={1} space={1}>
82
- {defaultLanguages.length > 0 && (
83
- <>
84
- {defaultLanguages.map((l) => (
85
- <LanguageFilterOption key={l.id} id={l.id} title={l.title} selected />
86
- ))}
87
- <Card borderTop />
88
- </>
89
- )}
90
-
91
- <Button
92
- mode="bleed"
93
- onClick={handleToggleAll}
94
- justify="flex-start"
95
- value={allSelected ? 'NONE' : 'ALL'}
96
- disabled={!!query}
97
- >
98
- <Flex gap={3} align="center">
99
- <Text size={2}>
100
- {allSelected ? (
101
- <TextWithTone tone="primary">
102
- <EyeClosedIcon />
103
- </TextWithTone>
104
- ) : (
105
- <EyeOpenIcon />
106
- )}
107
- </Text>
108
- <Box flex={1}>
109
- <Text>{allSelected ? 'Hide all' : 'Show all'}</Text>
110
- </Box>
111
- </Flex>
112
- </Button>
113
-
114
- {showSearch ? (
115
- <TextInput onChange={handleQuery} value={query} placeholder="Filter languages" />
116
- ) : (
117
- <Card borderTop />
118
- )}
119
-
120
- {languageOptions
121
- .filter((language) => {
122
- if (query) {
123
- return language.title.toLowerCase().includes(query.toLowerCase())
124
- }
125
- return true
126
- })
127
- .map((lang) => (
128
- <LanguageFilterOption
129
- id={lang.id}
130
- key={lang.id}
131
- onToggle={toggleLanguage}
132
- selected={activeLanguages.includes(lang.id)}
133
- title={lang.title}
134
- />
135
- ))}
136
- </Stack>
137
- </StyledBox>
138
- )
139
-
140
- const buttonText =
141
- activeLanguages.length === langCount
142
- ? 'Showing all'
143
- : `Showing ${activeLanguages.length} / ${langCount}`
144
- return (
145
- <Popover animate content={content} open={open} portal ref={setPopover}>
146
- <Button
147
- text={buttonText}
148
- icon={TranslateIcon}
149
- mode="bleed"
150
- onClick={handleClick}
151
- ref={setButton}
152
- selected={open}
153
- />
154
- </Popover>
155
- )
156
- }
157
-
158
- function LanguageFilterOption(props: {
159
- id: string
160
- selected: boolean
161
- title: string
162
- // eslint-disable-next-line react/require-default-props
163
- onToggle?: (id: string) => void
164
- }) {
165
- const {id, onToggle, selected, title} = props
166
-
167
- const handleChange = useCallback(() => {
168
- if (onToggle) {
169
- onToggle(id)
170
- }
171
- }, [id, onToggle])
172
-
173
- const disabled = !onToggle
174
-
175
- return (
176
- <Button mode="bleed" onClick={handleChange} justify="flex-start" disabled={disabled}>
177
- <Flex gap={3} align="center">
178
- <Text size={2}>
179
- {selected ? (
180
- <TextWithTone tone={disabled ? 'default' : 'positive'}>
181
- <CheckmarkCircleIcon />
182
- </TextWithTone>
183
- ) : (
184
- <CircleIcon />
185
- )}
186
- </Text>
187
- <Box flex={1}>
188
- <Text>{title}</Text>
189
- </Box>
190
- <Badge>{id}</Badge>
191
- </Flex>
192
- </Button>
193
- )
194
- }
@@ -1,52 +0,0 @@
1
- import {useMemo} from 'react'
2
- import {type ObjectInputProps, type ObjectMember, useFormValue, useSchema} from 'sanity'
3
-
4
- import {isLanguageFilterEnabled} from './filterField'
5
- import {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'
6
-
7
- // First check that this Object is in a schema type for which language-filter is enabled
8
- export function FilteredObjectWrapper(props: ObjectInputProps) {
9
- const {options} = useLanguageFilterStudioContext()
10
-
11
- const documentType = useFormValue(['_type']) as string
12
- const schema = useSchema()
13
- const languageFilterEnabled = isLanguageFilterEnabled(schema.get(documentType), options)
14
- return languageFilterEnabled ? <FilteredObjectInput {...props} /> : props.renderDefault(props)
15
- }
16
-
17
- // Modify the object members based on selected languages in the filter
18
- export function FilteredObjectInput(props: ObjectInputProps) {
19
- const {members: membersProp, schemaType, renderDefault, ...restProps} = props
20
- const {selectedLanguageIds, options} = useLanguageFilterStudioContext()
21
- const {filterField} = options
22
-
23
- const members: ObjectMember[] = useMemo(() => {
24
- return membersProp
25
- .filter((member) => {
26
- return (
27
- (member.kind === 'field' && filterField(schemaType, member, selectedLanguageIds)) ||
28
- member.kind === 'fieldSet' ||
29
- member.kind === 'error'
30
- )
31
- })
32
- .map((member) => {
33
- if (member.kind === 'fieldSet') {
34
- return {
35
- ...member,
36
- fieldSet: {
37
- ...member.fieldSet,
38
- members: member.fieldSet.members.filter((fieldsetMember) => {
39
- return (
40
- fieldsetMember.kind === 'field' &&
41
- filterField(schemaType, fieldsetMember, selectedLanguageIds)
42
- )
43
- }),
44
- },
45
- }
46
- }
47
- return member
48
- })
49
- }, [schemaType, membersProp, filterField, selectedLanguageIds])
50
-
51
- return renderDefault({...restProps, members, schemaType, renderDefault})
52
- }
@@ -1,92 +0,0 @@
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 {
6
- Language,
7
- LanguageCallback,
8
- LanguageFilterConfig,
9
- LanguageFilterConfigProcessed,
10
- } from './types'
11
- import {useSelectedLanguageIds} from './useSelectedLanguageIds'
12
-
13
- export interface LanguageFilterStudioContextProps {
14
- // eslint-disable-next-line react/require-default-props
15
- options: Required<LanguageFilterConfig>
16
- }
17
-
18
- export interface LanguageFilterStudioContextProcessed {
19
- options: Required<LanguageFilterConfigProcessed>
20
- }
21
-
22
- export interface LanguageFilterStudioContextValue extends LanguageFilterStudioContextProcessed {
23
- selectedLanguageIds: string[]
24
- setSelectedLanguageIds: (ids: string[]) => void
25
- }
26
-
27
- export const defaultContextValue: LanguageFilterStudioContextValue = {
28
- options: {
29
- apiVersion: '2022-11-27',
30
- supportedLanguages: [],
31
- defaultLanguages: [],
32
- documentTypes: [],
33
- filterField: defaultFilterField,
34
- },
35
- selectedLanguageIds: [],
36
- setSelectedLanguageIds: () => console.error('LanguageFilterStudioContext not initialized'),
37
- }
38
-
39
- const LanguageFilterStudioContext =
40
- createContext<LanguageFilterStudioContextValue>(defaultContextValue)
41
-
42
- /**
43
- * This is a separate Provider from the Context that wraps the document pane
44
- * but it used to listen to changes to the selected language IDs inside it
45
- * and provide them to a Studio-wide context
46
- */
47
- export function LanguageFilterStudioProvider(
48
- props: LayoutProps & LanguageFilterStudioContextProps,
49
- ) {
50
- const client = useClient({apiVersion: '2023-01-01'})
51
- const [languages, setLanguages] = useState<Language[]>(
52
- Array.isArray(props.options.supportedLanguages) ? props.options.supportedLanguages : [],
53
- )
54
- useEffect(() => {
55
- let asyncLanguages: Language[] = []
56
-
57
- async function getLanguages(supportedLanguagesCallback: LanguageCallback) {
58
- asyncLanguages = await supportedLanguagesCallback(client, {})
59
- setLanguages(asyncLanguages)
60
- }
61
-
62
- if (!Array.isArray(props.options.supportedLanguages)) {
63
- getLanguages(props.options.supportedLanguages)
64
- }
65
- }, [client, props.options.supportedLanguages])
66
-
67
- const options = useMemo<Required<LanguageFilterConfigProcessed>>(() => {
68
- return {
69
- ...defaultContextValue.options,
70
- ...props.options,
71
- supportedLanguages: languages,
72
- }
73
- }, [props.options, languages])
74
-
75
- const [selectedLanguageIds, setSelectedLanguageIds] = useSelectedLanguageIds(options)
76
-
77
- return (
78
- <LanguageFilterStudioContext.Provider
79
- value={{options, selectedLanguageIds, setSelectedLanguageIds}}
80
- >
81
- {props.renderDefault(props)}
82
- </LanguageFilterStudioContext.Provider>
83
- )
84
- }
85
-
86
- /**
87
- * Retrieves plugin options and the currently selected
88
- * language IDs from anywhere in the Studio
89
- */
90
- export function useLanguageFilterStudioContext() {
91
- return useContext(LanguageFilterStudioContext)
92
- }
@@ -1,100 +0,0 @@
1
- import type {FieldMember, ObjectSchemaType} from 'sanity'
2
-
3
- import {defaultFilterField, isLanguageFilterEnabled} from './filterField'
4
-
5
- describe('filterField', () => {
6
- describe('isLanguageFilterEnabled', () => {
7
- const docType: ObjectSchemaType = {
8
- name: 'some-doc',
9
- jsonType: 'object',
10
- fields: [],
11
- // eslint-disable-next-line camelcase
12
- __experimental_search: [],
13
- type: {
14
- name: 'document',
15
- jsonType: 'object',
16
- fields: [],
17
- // eslint-disable-next-line camelcase
18
- __experimental_search: [],
19
- },
20
- }
21
- it('should be enabled when documentTypes is missing', () => {
22
- const enabled = isLanguageFilterEnabled(docType, {supportedLanguages: []})
23
- expect(enabled).toBeTruthy()
24
- })
25
-
26
- it('should be disabled when documentTypes is missing and options.languageFilter: false', () => {
27
- const enabled = isLanguageFilterEnabled(
28
- {...docType, options: {languageFilter: false}},
29
- {supportedLanguages: []},
30
- )
31
- expect(enabled).toBeFalsy()
32
- })
33
-
34
- it('should be enabled when documentTypes is contains doc-type name', () => {
35
- const enabled = isLanguageFilterEnabled(
36
- {...docType, options: {languageFilter: false}},
37
- {supportedLanguages: [], documentTypes: [docType.name]},
38
- )
39
- expect(enabled).toBeTruthy()
40
- })
41
-
42
- it('should be enabled when documentTypes does not contain doc-type name, but options.languageFilter: true', () => {
43
- const enabled = isLanguageFilterEnabled(
44
- {...docType, options: {languageFilter: true}},
45
- {supportedLanguages: [], documentTypes: []},
46
- )
47
- expect(enabled).toBeTruthy()
48
- })
49
- })
50
-
51
- describe('defaultFilterField', () => {
52
- const localePrefixedObject: ObjectSchemaType = {
53
- name: 'locale_parent',
54
- jsonType: 'object',
55
- fields: [],
56
- // eslint-disable-next-line camelcase
57
- __experimental_search: [],
58
- }
59
- const member: FieldMember = {
60
- name: 'nb',
61
- key: 'nb',
62
- collapsed: undefined,
63
- collapsible: undefined,
64
- kind: 'field',
65
- open: true,
66
- index: 0,
67
- field: {
68
- schemaType: {name: 'string', jsonType: 'string'},
69
- level: 1,
70
- id: 'nb',
71
- path: [],
72
- validation: [],
73
- presence: [],
74
- changed: false,
75
- value: undefined,
76
- },
77
- groups: [],
78
- inSelectedGroup: false,
79
- }
80
-
81
- it('should filter -> true for nb field inside local-prefixed object', () => {
82
- const result = defaultFilterField(localePrefixedObject, member, ['nb'])
83
- expect(result).toBeTruthy()
84
- })
85
-
86
- it('should filter -> false for unselected field inside local-prefixed object', () => {
87
- const result = defaultFilterField(localePrefixedObject, member, ['other'])
88
- expect(result).toBeFalsy()
89
- })
90
-
91
- it('should filter -> true for nb field inside non-prefixed object', () => {
92
- const result = defaultFilterField(
93
- {...localePrefixedObject, name: 'not-start-with-locale-field'},
94
- member,
95
- ['nb'],
96
- )
97
- expect(result).toBeTruthy()
98
- })
99
- })
100
- })
@@ -1,35 +0,0 @@
1
- import type {SchemaType} from 'sanity'
2
-
3
- import type {FilterFieldFunction, LanguageFilterConfig, LanguageFilterSchema} from './types'
4
-
5
- export const defaultFilterField: FilterFieldFunction = (
6
- enclosingType,
7
- field,
8
- selectedLanguageIds,
9
- ) => !enclosingType.name.startsWith('locale') || selectedLanguageIds.includes(field.name)
10
-
11
- export function isLanguageFilterEnabled(
12
- schemaType: SchemaType | undefined,
13
- options: LanguageFilterConfig,
14
- ): boolean {
15
- const schemaFilter =
16
- isDocument(schemaType) && (schemaType as LanguageFilterSchema)?.options?.languageFilter
17
- const defaultEnabled = !options.documentTypes
18
-
19
- return !!(
20
- (defaultEnabled && schemaFilter !== false) ||
21
- (!defaultEnabled && schemaFilter) ||
22
- (schemaType && options.documentTypes?.includes(schemaType.name))
23
- )
24
- }
25
-
26
- function isDocument(schemaType?: SchemaType) {
27
- return schemaType?.jsonType === 'object' && getRootType(schemaType).name === 'document'
28
- }
29
-
30
- function getRootType(schema: SchemaType): SchemaType {
31
- if (schema.type) {
32
- return getRootType(schema.type)
33
- }
34
- return schema
35
- }
package/src/index.ts DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * Plugin function
3
- */
4
- export {defaultFilterField, isLanguageFilterEnabled} from './filterField'
5
- export {useLanguageFilterStudioContext} from './LanguageFilterStudioContext'
6
- export {languageFilter} from './plugin'
7
- export type {
8
- FilterFieldFunction,
9
- Language,
10
- LanguageFilterConfig,
11
- LanguageFilterOptions,
12
- LanguageFilterSchema,
13
- } from './types'
@@ -1,32 +0,0 @@
1
- export type LanguageSubscription = (ids: string[]) => void
2
- export type Unsubscribe = () => void
3
- export type LanguageSubscribe = (subscription: LanguageSubscription) => Unsubscribe
4
-
5
- export interface SelectedLanguageIdsBus {
6
- onSelectedIdsChange: (ids: string[]) => void
7
- subscribeSelectedIds: LanguageSubscribe
8
- }
9
-
10
- /**
11
- * We need a way to communicate state changes between the pane menu and input components.
12
- * LanguageFilter button lives outside the input-render tree, so Context is out.
13
- * This is a workaround for that.
14
- */
15
- export function createSelectedLanguageIdsBus(): SelectedLanguageIdsBus {
16
- const subs: LanguageSubscription[] = []
17
-
18
- const onSelectedIdsChange = (ids: string[]) => {
19
- subs.forEach((s) => s(ids))
20
- }
21
- const subscribeSelectedIds = (subscription: LanguageSubscription) => {
22
- subs.push(subscription)
23
- return () => {
24
- subs.splice(subs.indexOf(subscription), 1)
25
- }
26
- }
27
-
28
- return {
29
- onSelectedIdsChange,
30
- subscribeSelectedIds,
31
- }
32
- }