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.
- package/README.md +38 -5
- package/lib/index.esm.js +2 -0
- package/lib/index.esm.js.map +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/src/index.d.ts +16 -0
- package/package.json +56 -42
- package/src/components/{InternationalizedArrayInput.tsx → InternationalizedArray.tsx} +43 -72
- package/src/components/InternationalizedInput.tsx +108 -0
- package/src/components/createFieldName.ts +3 -3
- package/src/components/getToneFromValidation.ts +5 -5
- package/src/plugin.tsx +2 -2
- package/src/schema/array.ts +17 -6
- package/src/schema/object.ts +20 -3
- package/src/types.ts +2 -2
- package/lib/cjs/index.js +0 -494
- package/lib/cjs/index.js.map +0 -1
- package/lib/esm/index.js +0 -487
- package/lib/esm/index.js.map +0 -1
- package/lib/types/index.d.ts +0 -12
- package/lib/types/index.d.ts.map +0 -1
|
@@ -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:
|
|
5
|
-
if (!validations
|
|
6
|
-
return
|
|
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
|
|
17
|
+
return undefined
|
|
18
18
|
}
|
package/src/plugin.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
11
|
+
export const internationalizedArray = definePlugin<PluginConfig>((config = CONFIG_DEFAULT) => {
|
|
12
12
|
const {languages, fieldTypes} = {...CONFIG_DEFAULT, ...config}
|
|
13
13
|
|
|
14
14
|
return {
|
package/src/schema/array.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {defineField,
|
|
1
|
+
import {defineField, FieldDefinition, Rule} from 'sanity'
|
|
2
2
|
|
|
3
3
|
import {createFieldName} from '../components/createFieldName'
|
|
4
|
-
import
|
|
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 |
|
|
9
|
+
type: string | FieldDefinition
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export default (config: ArrayFactoryConfig):
|
|
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
|
-
|
|
22
|
+
// TODO: Resolve this typing issue with the outer component
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
components: {
|
|
25
|
+
input: InternationalizedArray,
|
|
26
|
+
},
|
|
23
27
|
options: {languages},
|
|
24
|
-
|
|
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 ?? {}
|
package/src/schema/object.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {defineField,
|
|
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 |
|
|
7
|
+
type: string | FieldDefinition
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
export default (config: ObjectFactoryConfig):
|
|
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,
|
|
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 |
|
|
29
|
+
fieldTypes: (string | RuleTypeConstraint)[]
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export type ArraySchemaWithLanguageOptions = ArraySchemaType & {
|