sanity-plugin-internationalized-array 1.1.1 → 1.2.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.
@@ -0,0 +1,108 @@
1
+ import {ObjectItemProps, useFormValue} from 'sanity'
2
+ import React, {useCallback, useMemo} from 'react'
3
+ import {unset, set} from 'sanity'
4
+ import {Box, Button, Flex, Label, MenuButton, Menu, MenuItem, Card} from '@sanity/ui'
5
+ import {RemoveIcon} from '@sanity/icons'
6
+
7
+ import {Language} from '../types'
8
+ import {getToneFromValidation} from './getToneFromValidation'
9
+
10
+ type InternationalizedValue = {
11
+ _type: string
12
+ _key: string
13
+ value: string
14
+ }
15
+
16
+ export default function InternationalizedInput(props: ObjectItemProps<InternationalizedValue>) {
17
+ const parentValue = useFormValue(props.path.slice(0, -1)) as InternationalizedValue[]
18
+
19
+ const inlineProps = {
20
+ ...props.inputProps,
21
+ // This is the magic that makes inline editing work
22
+ members: props.inputProps.members.filter((m) => m.kind === 'field' && m.name === 'value'),
23
+ // This just overrides the type
24
+ // TODO: Remove this as it shouldn't be necessary
25
+ value: props.value as InternationalizedValue,
26
+ }
27
+
28
+ const {validation, value, onChange, readOnly} = inlineProps
29
+
30
+ // The parent array contains the languages from the plugin config
31
+ // TODO: fix TS support for overloading options
32
+ const languages: Language[] = useMemo(
33
+ // @ts-ignore
34
+ () => props?.parentSchemaType?.options?.languages ?? [],
35
+ // @ts-ignore
36
+ [props?.parentSchemaType?.options?.languages]
37
+ )
38
+ const languageKeysInUse = useMemo(() => parentValue?.map((v) => v._key) ?? [], [parentValue])
39
+ const keyIsValid = languages.find((l) => l.id === value._key)
40
+
41
+ // Changes the key of this item, ideally to a valid language
42
+ const handleKeyChange = useCallback(
43
+ (languageId: string) => {
44
+ if (!value || !languages.find((l) => l.id === languageId)) {
45
+ return
46
+ }
47
+
48
+ onChange([set(languageId, ['_key'])])
49
+ },
50
+ [onChange, value, languages]
51
+ )
52
+
53
+ // Removes this item from the array
54
+ const handleUnset = useCallback(() => {
55
+ onChange(unset())
56
+ }, [onChange])
57
+
58
+ return (
59
+ <Card tone={getToneFromValidation(validation)}>
60
+ <Flex align="flex-end" gap={1}>
61
+ <Card tone="inherit">
62
+ <Box paddingY={3} paddingRight={2}>
63
+ {keyIsValid ? (
64
+ <Label muted size={1}>
65
+ {value._key}
66
+ </Label>
67
+ ) : (
68
+ <MenuButton
69
+ button={<Button fontSize={1} text={`Change "${value._key}"`} />}
70
+ id={`${value._key}-change-key`}
71
+ menu={
72
+ <Menu>
73
+ {languages.map((language) => (
74
+ <MenuItem
75
+ // TODO: Prevent changing to a key that already exists in the array
76
+ disabled={languageKeysInUse.includes(language.id)}
77
+ fontSize={1}
78
+ key={language.id}
79
+ text={language.id.toLocaleUpperCase()}
80
+ onClick={() => handleKeyChange(language.id)}
81
+ />
82
+ ))}
83
+ </Menu>
84
+ }
85
+ placement="right"
86
+ popover={{portal: true}}
87
+ />
88
+ )}
89
+ </Box>
90
+ </Card>
91
+
92
+ <Card paddingRight={2} flex={1} tone="inherit">
93
+ {props.inputProps.renderInput(props.inputProps)}
94
+ </Card>
95
+
96
+ <Card tone="inherit">
97
+ <Button
98
+ mode="ghost"
99
+ icon={RemoveIcon}
100
+ tone="critical"
101
+ disabled={readOnly}
102
+ onClick={handleUnset}
103
+ />
104
+ </Card>
105
+ </Flex>
106
+ </Card>
107
+ )
108
+ }
@@ -1,15 +1,15 @@
1
- export function camelCase(string: string) {
1
+ export function camelCase(string: string): string {
2
2
  return string.replace(/-([a-z])/g, (g) => g[1].toUpperCase())
3
3
  }
4
4
 
5
- export function titleCase(string: string) {
5
+ export function titleCase(string: string): string {
6
6
  return string
7
7
  .split(` `)
8
8
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
9
9
  .join(` `)
10
10
  }
11
11
 
12
- export function pascalCase(string: string) {
12
+ export function pascalCase(string: string): string {
13
13
  return titleCase(camelCase(string))
14
14
  }
15
15
 
@@ -1,9 +1,9 @@
1
- import {NodeValidation} from 'sanity/form'
2
1
  import {CardTone} from '@sanity/ui'
2
+ import {FormNodeValidation} from 'sanity'
3
3
 
4
- export function getToneFromValidation(validations: NodeValidation[]): CardTone {
5
- if (!validations.length) {
6
- return `default`
4
+ export function getToneFromValidation(validations: FormNodeValidation[]): CardTone | undefined {
5
+ if (!validations?.length) {
6
+ return undefined
7
7
  }
8
8
 
9
9
  const validationLevels = validations.map((v) => v.level)
@@ -14,5 +14,5 @@ export function getToneFromValidation(validations: NodeValidation[]): CardTone {
14
14
  return `caution`
15
15
  }
16
16
 
17
- return `default`
17
+ return undefined
18
18
  }
package/src/plugin.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import {createPlugin} from 'sanity'
1
+ import {definePlugin} from 'sanity'
2
2
  import {PluginConfig} from './types'
3
3
  import array from './schema/array'
4
4
  import object from './schema/object'
@@ -8,7 +8,7 @@ const CONFIG_DEFAULT = {
8
8
  fieldTypes: [],
9
9
  }
10
10
 
11
- export const internationalizedArray = createPlugin<PluginConfig>((config = CONFIG_DEFAULT) => {
11
+ export const internationalizedArray = definePlugin<PluginConfig>((config = CONFIG_DEFAULT) => {
12
12
  const {languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}
13
13
 
14
14
  return {
@@ -1,15 +1,15 @@
1
- import {defineField, Rule, Schema} from 'sanity'
1
+ import {defineField, FieldDefinition, Rule} from 'sanity'
2
2
 
3
3
  import {createFieldName} from '../components/createFieldName'
4
- import InternationalizedArrayInput from '../components/InternationalizedArrayInput'
4
+ import InternationalizedArray from '../components/InternationalizedArray'
5
5
  import {Language, Value} from '../types'
6
6
 
7
7
  type ArrayFactoryConfig = {
8
8
  languages: Language[]
9
- type: string | Schema.FieldDefinition
9
+ type: string | FieldDefinition
10
10
  }
11
11
 
12
- export default (config: ArrayFactoryConfig): Schema.FieldDefinition<'array'> => {
12
+ export default (config: ArrayFactoryConfig): FieldDefinition<'array'> => {
13
13
  const {languages, type} = config
14
14
  const typeName = typeof type === `string` ? type : type.name
15
15
  const arrayName = createFieldName(typeName)
@@ -19,9 +19,20 @@ export default (config: ArrayFactoryConfig): Schema.FieldDefinition<'array'> =>
19
19
  name: arrayName,
20
20
  title: 'Internationalized array',
21
21
  type: 'array',
22
- components: {input: InternationalizedArrayInput},
22
+ // TODO: Resolve this typing issue with the outer component
23
+ // @ts-ignore
24
+ components: {
25
+ input: InternationalizedArray,
26
+ },
23
27
  options: {languages},
24
- of: [defineField({name: objectName, type: objectName})],
28
+ // TODO: Resolve this typing issue with the inner object
29
+ // @ts-ignore
30
+ of: [
31
+ defineField({
32
+ name: objectName,
33
+ type: objectName,
34
+ }),
35
+ ],
25
36
  validation: (rule: Rule) =>
26
37
  rule.max(languages?.length).custom<Value[]>((value, context) => {
27
38
  const {languages: contextLanguages}: {languages: Language[]} = context?.type?.options ?? {}
@@ -1,12 +1,13 @@
1
- import {defineField, Schema} from 'sanity'
1
+ import {defineField, FieldDefinition} from 'sanity'
2
2
 
3
3
  import {createFieldName} from '../components/createFieldName'
4
+ import InternationalizedInput from '../components/InternationalizedInput'
4
5
 
5
6
  type ObjectFactoryConfig = {
6
- type: string | Schema.FieldDefinition
7
+ type: string | FieldDefinition
7
8
  }
8
9
 
9
- export default (config: ObjectFactoryConfig): Schema.FieldDefinition<'object'> => {
10
+ export default (config: ObjectFactoryConfig): FieldDefinition<'object'> => {
10
11
  const {type} = config
11
12
  const typeName = typeof type === `string` ? type : type.name
12
13
  const objectName = createFieldName(typeName, true)
@@ -15,6 +16,16 @@ export default (config: ObjectFactoryConfig): Schema.FieldDefinition<'object'> =
15
16
  name: objectName,
16
17
  title: `Internationalized array ${type}`,
17
18
  type: 'object',
19
+ // TODO: Resolve this typing issue with the return type
20
+ // @ts-ignore
21
+ components: {
22
+ // item: InternationalizedInputWrapper,
23
+ // TODO: Resolve this typing issue with the outer component
24
+ // @ts-ignore
25
+ item: InternationalizedInput,
26
+ },
27
+ // TODO: Address this typing issue with the inner object
28
+ // @ts-ignore
18
29
  fields: [
19
30
  typeof type === `string`
20
31
  ? // Define a basic field if all we have is the string name
@@ -25,5 +36,11 @@ export default (config: ObjectFactoryConfig): Schema.FieldDefinition<'object'> =
25
36
  : // Pass in the configured options, but overwrite the name
26
37
  {...type, name: 'value'},
27
38
  ],
39
+ preview: {
40
+ select: {
41
+ title: 'value',
42
+ subtitle: '_key',
43
+ },
44
+ },
28
45
  })
29
46
  }
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {Rule, ArraySchemaType, Schema} from 'sanity'
1
+ import {Rule, ArraySchemaType, RuleTypeConstraint} from 'sanity'
2
2
 
3
3
  export type Language = {
4
4
  id: string
@@ -26,7 +26,7 @@ export type Value = {
26
26
 
27
27
  export type PluginConfig = {
28
28
  languages: Language[]
29
- fieldTypes: (string | Schema.FieldDefinition)[]
29
+ fieldTypes: (string | RuleTypeConstraint)[]
30
30
  }
31
31
 
32
32
  export type ArraySchemaWithLanguageOptions = ArraySchemaType & {