sanity-plugin-internationalized-array 1.5.0 → 1.6.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.
@@ -1,4 +1,5 @@
1
1
  import type {ArraySchemaType} from 'sanity'
2
+ import type {FieldDefinition} from 'sanity'
2
3
  import {Plugin as Plugin_2} from 'sanity'
3
4
  import type {Rule} from 'sanity'
4
5
  import type {RuleTypeConstraint} from 'sanity'
@@ -25,7 +26,8 @@ export declare type ArrayConfig = {
25
26
 
26
27
  export declare type ArraySchemaWithLanguageOptions = ArraySchemaType & {
27
28
  options: {
28
- languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
29
+ select?: Record<string, string>
30
+ languages: Language[] | LanguageCallback
29
31
  apiVersion: string
30
32
  }
31
33
  }
@@ -39,12 +41,30 @@ export declare type Language = {
39
41
  title: string
40
42
  }
41
43
 
44
+ export declare type LanguageCallback = (
45
+ client: SanityClient,
46
+ selectedValue: Record<string, unknown>
47
+ ) => Promise<Language[]>
48
+
42
49
  export declare type PluginConfig = {
43
50
  /**
44
51
  * https://www.sanity.io/docs/api-versioning
45
52
  * @defaultValue '2022-11-27'
46
53
  */
47
54
  apiVersion?: string
55
+ /**
56
+ * Specify fields that should be available in the language callback:
57
+ * ```tsx
58
+ * {
59
+ * select: {
60
+ * markets: 'markets'
61
+ * },
62
+ * languages: (client, {markets}) =>
63
+ * query.fetch(groq`*[_type == "language" && market in $markets]{id,title}`, {markets})
64
+ * }
65
+ * ```
66
+ */
67
+ select?: Record<string, string>
48
68
  /**
49
69
  * You can give it an array of language definitions:
50
70
  * ```tsx
@@ -72,7 +92,7 @@ export declare type PluginConfig = {
72
92
  * }
73
93
  * ```
74
94
  */
75
- languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
95
+ languages: Language[] | LanguageCallback
76
96
  /**
77
97
  * Can be a string matching core field types, as well as custom ones:
78
98
  * ```tsx
@@ -96,7 +116,7 @@ export declare type PluginConfig = {
96
116
  * }
97
117
  * ```
98
118
  */
99
- fieldTypes: (string | RuleTypeConstraint)[]
119
+ fieldTypes: (string | RuleTypeConstraint | FieldDefinition)[]
100
120
  }
101
121
 
102
122
  export declare type Value = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-internationalized-array",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Store localized fields in an array to save on attributes",
5
5
  "keywords": [
6
6
  "sanity",
@@ -50,6 +50,8 @@
50
50
  "@sanity/icons": "^2.0.0",
51
51
  "@sanity/incompatible-plugin": "^1.0.4",
52
52
  "@sanity/ui": "^1.0.0",
53
+ "fast-deep-equal": "^3.1.3",
54
+ "lodash.get": "^4.4.2",
53
55
  "suspend-react": "^0.0.8"
54
56
  },
55
57
  "devDependencies": {
@@ -58,6 +60,7 @@
58
60
  "@sanity/pkg-utils": "^1.18.0",
59
61
  "@sanity/plugin-kit": "^2.1.17",
60
62
  "@sanity/semantic-release-preset": "^2.0.2",
63
+ "@types/lodash.get": "^4.4.7",
61
64
  "@types/styled-components": "^5.1.26",
62
65
  "@typescript-eslint/eslint-plugin": "^5.44.0",
63
66
  "@typescript-eslint/parser": "^5.44.0",
package/src/cache.ts CHANGED
@@ -15,4 +15,5 @@ export const preload = (fn: () => Promise<Language[]>) =>
15
15
  export const clear = () => suspend.clear([version, namespace])
16
16
 
17
17
  // https://github.com/pmndrs/suspend-react#peeking-into-entries-outside-of-suspense
18
- export const peek = () => suspend.peek([version, namespace]) as Language[] | undefined
18
+ export const peek = (selectedValue: Record<string, unknown>) =>
19
+ suspend.peek([version, namespace, selectedValue]) as Language[] | undefined
@@ -1,4 +1,4 @@
1
- import React, {Fragment, useCallback, useEffect, useMemo} from 'react'
1
+ import React, {useCallback, useDeferredValue, useEffect, useMemo} from 'react'
2
2
  import {
3
3
  insert,
4
4
  set,
@@ -7,16 +7,19 @@ import {
7
7
  ArrayOfObjectsItem,
8
8
  ArrayOfObjectsInputProps,
9
9
  useClient,
10
+ useFormBuilder,
10
11
  } from 'sanity'
11
12
  import {Button, Grid, Stack, useToast} from '@sanity/ui'
12
13
  import {AddIcon} from '@sanity/icons'
13
14
  import {suspend} from 'suspend-react'
15
+ import equal from 'fast-deep-equal'
14
16
 
15
17
  import type {Value, ArraySchemaWithLanguageOptions} from '../types'
16
18
  import Feedback from './Feedback'
17
19
  // TODO: Move this provider to the root component
18
20
  import {LanguageProvider} from './languageContext'
19
21
  import {namespace, version} from '../cache'
22
+ import {getSelectedValue} from './getSelectedValue'
20
23
 
21
24
  export type InternationalizedArrayProps = ArrayOfObjectsInputProps<
22
25
  Value,
@@ -28,18 +31,28 @@ export default function InternationalizedArray(props: InternationalizedArrayProp
28
31
  const readOnly = typeof schemaType.readOnly === 'boolean' ? schemaType.readOnly : false
29
32
  const {options} = schemaType
30
33
  const toast = useToast()
34
+ const {value: document} = useFormBuilder()
35
+ const deferredDocument = useDeferredValue(document)
36
+ const selectedValue = useMemo(
37
+ () => getSelectedValue(options.select, deferredDocument),
38
+ [options.select, deferredDocument]
39
+ )
31
40
 
32
41
  const {apiVersion} = options
33
42
  const client = useClient({apiVersion})
34
43
  const languages = Array.isArray(options.languages)
35
44
  ? options.languages
36
- : // eslint-disable-next-line require-await
37
- suspend(async () => {
38
- if (typeof options.languages === 'function') {
39
- return options.languages(client)
40
- }
41
- return options.languages
42
- }, [version, namespace])
45
+ : suspend(
46
+ // eslint-disable-next-line require-await
47
+ async () => {
48
+ if (typeof options.languages === 'function') {
49
+ return options.languages(client, selectedValue)
50
+ }
51
+ return options.languages
52
+ },
53
+ [version, namespace, selectedValue],
54
+ {equal}
55
+ )
43
56
 
44
57
  const handleAddLanguage = useCallback(
45
58
  (languageId?: string) => {
@@ -7,10 +7,10 @@ export default memo(function Preload(
7
7
  props: Required<Pick<PluginConfig, 'apiVersion' | 'languages'>>
8
8
  ) {
9
9
  const client = useClient({apiVersion: props.apiVersion})
10
- if (!Array.isArray(peek())) {
10
+ if (!Array.isArray(peek({}))) {
11
11
  // eslint-disable-next-line require-await
12
12
  preload(async () =>
13
- Array.isArray(props.languages) ? props.languages : props.languages(client)
13
+ Array.isArray(props.languages) ? props.languages : props.languages(client, {})
14
14
  )
15
15
  }
16
16
 
@@ -0,0 +1,29 @@
1
+ import {get} from 'lodash'
2
+
3
+ export const getSelectedValue = (
4
+ select: Record<string, string> | undefined,
5
+ document:
6
+ | {
7
+ [x: string]: unknown
8
+ }
9
+ | undefined
10
+ ): Record<string, unknown> => {
11
+ if (!select || !document) {
12
+ return {}
13
+ }
14
+
15
+ const selection: Record<string, string> = select || {}
16
+ const selectedValue: Record<string, unknown> = {}
17
+ for (const [key, path] of Object.entries(selection)) {
18
+ let value = get(document, path)
19
+ if (Array.isArray(value)) {
20
+ // If there are references in the array, ensure they have `_ref` set, otherwise they are considered empty and can safely be ignored
21
+ value = value.filter((item) =>
22
+ typeof item === 'object' ? item?._type === 'reference' && '_ref' in item : true
23
+ )
24
+ }
25
+ selectedValue[key] = value
26
+ }
27
+
28
+ return selectedValue
29
+ }
package/src/plugin.tsx CHANGED
@@ -11,7 +11,7 @@ const CONFIG_DEFAULT: PluginConfig = {
11
11
  }
12
12
 
13
13
  export const internationalizedArray = definePlugin<PluginConfig>((config = CONFIG_DEFAULT) => {
14
- const {apiVersion = '2022-11-27', languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}
14
+ const {apiVersion = '2022-11-27', select, languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}
15
15
 
16
16
  return {
17
17
  name: 'sanity-plugin-internationalized-array',
@@ -30,7 +30,7 @@ export const internationalizedArray = definePlugin<PluginConfig>((config = CONFI
30
30
  },
31
31
  schema: {
32
32
  types: [
33
- ...fieldTypes.map((type) => array({type, apiVersion, languages})),
33
+ ...fieldTypes.map((type) => array({type, apiVersion, select, languages})),
34
34
  ...fieldTypes.map((type) => object({type})),
35
35
  ],
36
36
  },
@@ -1,19 +1,21 @@
1
1
  /* eslint-disable no-nested-ternary */
2
- import {defineField, type FieldDefinition, type Rule, type SanityClient} from 'sanity'
2
+ import {defineField, type FieldDefinition, type Rule} from 'sanity'
3
3
  import {peek} from '../cache'
4
4
 
5
5
  import {createFieldName} from '../components/createFieldName'
6
+ import {getSelectedValue} from '../components/getSelectedValue'
6
7
  import InternationalizedArray from '../components/InternationalizedArray'
7
- import {Language, Value} from '../types'
8
+ import {Language, LanguageCallback, Value} from '../types'
8
9
 
9
10
  type ArrayFactoryConfig = {
10
11
  apiVersion: string
11
- languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
12
+ select?: Record<string, string>
13
+ languages: Language[] | LanguageCallback
12
14
  type: string | FieldDefinition
13
15
  }
14
16
 
15
17
  export default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {
16
- const {apiVersion, languages, type} = config
18
+ const {apiVersion, select, languages, type} = config
17
19
  const typeName = typeof type === `string` ? type : type.name
18
20
  const arrayName = createFieldName(typeName)
19
21
  const objectName = createFieldName(typeName, true)
@@ -27,7 +29,7 @@ export default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {
27
29
  components: {
28
30
  input: InternationalizedArray,
29
31
  },
30
- options: {apiVersion, languages},
32
+ options: {apiVersion, select, languages},
31
33
  // TODO: Resolve this typing issue with the inner object
32
34
  // @ts-ignore
33
35
  of: [
@@ -43,12 +45,13 @@ export default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {
43
45
  return true
44
46
  }
45
47
 
48
+ const selectedValue = getSelectedValue(select, context.document)
46
49
  const client = context.getClient({apiVersion})
47
50
  const contextLanguages: Language[] = Array.isArray(context?.type?.options?.languages)
48
51
  ? context!.type!.options.languages
49
- : Array.isArray(peek())
50
- ? peek()
51
- : await context?.type?.options.languages(client)
52
+ : Array.isArray(peek(selectedValue))
53
+ ? peek(selectedValue)
54
+ : await context?.type?.options.languages(client, selectedValue)
52
55
 
53
56
  if (value && value.length > contextLanguages.length) {
54
57
  return `Cannot be more than ${
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {Rule, ArraySchemaType, RuleTypeConstraint, SanityClient} from 'sanity'
1
+ import type {Rule, ArraySchemaType, RuleTypeConstraint, SanityClient, FieldDefinition} from 'sanity'
2
2
 
3
3
  export type Language = {
4
4
  id: Intl.UnicodeBCP47LocaleIdentifier
@@ -24,12 +24,30 @@ export type Value = {
24
24
  value?: string
25
25
  }
26
26
 
27
+ export type LanguageCallback = (
28
+ client: SanityClient,
29
+ selectedValue: Record<string, unknown>
30
+ ) => Promise<Language[]>
31
+
27
32
  export type PluginConfig = {
28
33
  /**
29
34
  * https://www.sanity.io/docs/api-versioning
30
35
  * @defaultValue '2022-11-27'
31
36
  */
32
37
  apiVersion?: string
38
+ /**
39
+ * Specify fields that should be available in the language callback:
40
+ * ```tsx
41
+ * {
42
+ * select: {
43
+ * markets: 'markets'
44
+ * },
45
+ * languages: (client, {markets}) =>
46
+ * query.fetch(groq`*[_type == "language" && market in $markets]{id,title}`, {markets})
47
+ * }
48
+ * ```
49
+ */
50
+ select?: Record<string, string>
33
51
  /**
34
52
  * You can give it an array of language definitions:
35
53
  * ```tsx
@@ -57,7 +75,7 @@ export type PluginConfig = {
57
75
  * }
58
76
  * ```
59
77
  */
60
- languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
78
+ languages: Language[] | LanguageCallback
61
79
  /**
62
80
  * Can be a string matching core field types, as well as custom ones:
63
81
  * ```tsx
@@ -81,12 +99,13 @@ export type PluginConfig = {
81
99
  * }
82
100
  * ```
83
101
  */
84
- fieldTypes: (string | RuleTypeConstraint)[]
102
+ fieldTypes: (string | RuleTypeConstraint | FieldDefinition)[]
85
103
  }
86
104
 
87
105
  export type ArraySchemaWithLanguageOptions = ArraySchemaType & {
88
106
  options: {
89
- languages: Language[] | ((client: SanityClient) => Promise<Language[]>)
107
+ select?: Record<string, string>
108
+ languages: Language[] | LanguageCallback
90
109
  apiVersion: string
91
110
  }
92
111
  }