sanity-plugin-internationalized-array 0.0.7 → 1.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/README.md +19 -15
- package/lib/cjs/index.js +468 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/esm/index.js +449 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/types/index.d.ts +19 -0
- package/lib/types/index.d.ts.map +1 -0
- package/package.json +47 -34
- package/sanity.json +7 -6
- package/src/components/Feedback.tsx +27 -0
- package/src/components/InternationalizedArrayInput.tsx +220 -0
- package/src/{LanguageArray → components}/Table.tsx +0 -0
- package/src/components/getToneFromValidation.ts +18 -0
- package/src/index.tsx +51 -0
- package/src/internationalizedArray.ts +80 -53
- package/src/types.ts +17 -9
- package/v2-incompatible.js +11 -0
- package/lib/LanguageArray/Table.js +0 -88
- package/lib/LanguageArray/Table.js.map +0 -1
- package/lib/LanguageArray/ValueInput.js +0 -17
- package/lib/LanguageArray/ValueInput.js.map +0 -1
- package/lib/LanguageArray/index.js +0 -253
- package/lib/LanguageArray/index.js.map +0 -1
- package/lib/hooks/useUnsetInputComponent.js +0 -32
- package/lib/hooks/useUnsetInputComponent.js.map +0 -1
- package/lib/index.js +0 -13
- package/lib/index.js.map +0 -1
- package/lib/internationalizedArray.js +0 -105
- package/lib/internationalizedArray.js.map +0 -1
- package/lib/types.js +0 -2
- package/lib/types.js.map +0 -1
- package/migrations/transformObjectToArray.js +0 -94
- package/src/LanguageArray/ValueInput.tsx +0 -6
- package/src/LanguageArray/index.tsx +0 -311
- package/src/hooks/useUnsetInputComponent.tsx +0 -17
- package/src/index.ts +0 -3
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {Text, Card, Stack, Code} from '@sanity/ui'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
const schemaExample = {
|
|
5
|
+
languages: [
|
|
6
|
+
{id: 'en', title: 'English'},
|
|
7
|
+
{id: 'no', title: 'Norsk'},
|
|
8
|
+
],
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function Feedback() {
|
|
12
|
+
return (
|
|
13
|
+
<Card tone="caution" border radius={2} padding={3}>
|
|
14
|
+
<Stack space={4}>
|
|
15
|
+
<Text>
|
|
16
|
+
An array of language objects must be passed into the <code>internationalizedArray</code>{' '}
|
|
17
|
+
helper function, each with an <code>id</code> and <code>title</code> field. Example:
|
|
18
|
+
</Text>
|
|
19
|
+
<Card padding={2} border radius={2}>
|
|
20
|
+
<Code size={1} language="javascript">
|
|
21
|
+
{JSON.stringify(schemaExample, null, 2)}
|
|
22
|
+
</Code>
|
|
23
|
+
</Card>
|
|
24
|
+
</Stack>
|
|
25
|
+
</Card>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import React, {useCallback, useMemo} from 'react'
|
|
2
|
+
import {
|
|
3
|
+
PatchEvent,
|
|
4
|
+
ArrayOfObjectsInputProps,
|
|
5
|
+
MemberItem,
|
|
6
|
+
unset,
|
|
7
|
+
insert,
|
|
8
|
+
set,
|
|
9
|
+
setIfMissing,
|
|
10
|
+
FormFieldValidationStatus,
|
|
11
|
+
} from 'sanity/form'
|
|
12
|
+
import {Box, Button, Flex, Grid, Label, Stack} from '@sanity/ui'
|
|
13
|
+
|
|
14
|
+
import {Language, Value, ArraySchemaWithLanguageOptions} from '../types'
|
|
15
|
+
import {Table, TableCell, TableRow} from './Table'
|
|
16
|
+
import {AddIcon, RemoveIcon, RestoreIcon} from '@sanity/icons'
|
|
17
|
+
import Feedback from './Feedback'
|
|
18
|
+
import {getToneFromValidation} from './getToneFromValidation'
|
|
19
|
+
|
|
20
|
+
export type InternationalizedArrayInputProps = ArrayOfObjectsInputProps<
|
|
21
|
+
Value,
|
|
22
|
+
ArraySchemaWithLanguageOptions
|
|
23
|
+
>
|
|
24
|
+
|
|
25
|
+
export default function InternationalizedArrayInput(props: InternationalizedArrayInputProps) {
|
|
26
|
+
const {members, value, schemaType, onChange} = props
|
|
27
|
+
const readOnly = typeof schemaType.readOnly === 'boolean' ? schemaType.readOnly : false
|
|
28
|
+
const {options} = schemaType
|
|
29
|
+
|
|
30
|
+
const languages: Language[] = useMemo(() => options?.languages ?? [], [options])
|
|
31
|
+
|
|
32
|
+
const handleAddLanguage = useCallback(
|
|
33
|
+
(languageId?: string) => {
|
|
34
|
+
// Create new items
|
|
35
|
+
const newItems = languageId
|
|
36
|
+
? // Just one for this language
|
|
37
|
+
[{_key: languageId}]
|
|
38
|
+
: // Or one for every missing language
|
|
39
|
+
languages
|
|
40
|
+
.filter((language) =>
|
|
41
|
+
value?.length ? !value.find((v) => v._key === language.id) : true
|
|
42
|
+
)
|
|
43
|
+
.map((language) => ({_key: language.id}))
|
|
44
|
+
|
|
45
|
+
// Insert new items in the correct order
|
|
46
|
+
const languagesInUse = value?.length ? value.map((v) => v) : []
|
|
47
|
+
|
|
48
|
+
const insertions = newItems.map((item) => {
|
|
49
|
+
// What's the original index of this language?
|
|
50
|
+
const languageIndex = languages.findIndex((l) => item._key === l.id)
|
|
51
|
+
|
|
52
|
+
// What languages are there beyond that index?
|
|
53
|
+
const remainingLanguages = languages.slice(languageIndex + 1)
|
|
54
|
+
|
|
55
|
+
// So what is the index in the current value array of the next language in the language array?
|
|
56
|
+
const nextLanguageIndex = languagesInUse.findIndex((l) =>
|
|
57
|
+
remainingLanguages.find((r) => r.id === l._key)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
// Keep local state up to date incase multiple insertions are being made
|
|
61
|
+
if (nextLanguageIndex < 0) {
|
|
62
|
+
languagesInUse.push(item)
|
|
63
|
+
} else {
|
|
64
|
+
languagesInUse.splice(nextLanguageIndex, 0, item)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return nextLanguageIndex < 0
|
|
68
|
+
? // No next language (-1), add to end of array
|
|
69
|
+
insert([item], 'after', [nextLanguageIndex])
|
|
70
|
+
: // Next language found, insert before that
|
|
71
|
+
insert([item], 'before', [nextLanguageIndex])
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
onChange([setIfMissing([]), ...insertions])
|
|
75
|
+
},
|
|
76
|
+
[languages, onChange, value]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
const handleUnsetByKey = useCallback(
|
|
80
|
+
(_key) => {
|
|
81
|
+
onChange(unset([{_key}]))
|
|
82
|
+
},
|
|
83
|
+
[onChange]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// TODO: This is lazy, reordering and re-setting the whole array – it could be surgical
|
|
87
|
+
const handleRestoreOrder = useCallback(() => {
|
|
88
|
+
if (!value?.length) {
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Create a new value array in the correct order
|
|
93
|
+
// This would also strip out values that don't have a language as the key
|
|
94
|
+
const updatedValue = value
|
|
95
|
+
.reduce((acc, v) => {
|
|
96
|
+
const newIndex = languages.findIndex((l) => l.id === v?._key)
|
|
97
|
+
|
|
98
|
+
if (newIndex) {
|
|
99
|
+
acc[newIndex] = v
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return acc
|
|
103
|
+
}, [] as Value[])
|
|
104
|
+
.filter(Boolean)
|
|
105
|
+
|
|
106
|
+
onChange(set(updatedValue))
|
|
107
|
+
}, [languages, onChange, value])
|
|
108
|
+
|
|
109
|
+
const allKeysAreLanguages = useMemo(() => {
|
|
110
|
+
return value?.every((v) => languages.find((l) => l?.id === v?._key))
|
|
111
|
+
}, [value, languages])
|
|
112
|
+
|
|
113
|
+
// Check languages are in the correct order
|
|
114
|
+
const languagesOutOfOrder = useMemo(() => {
|
|
115
|
+
if (!value?.length) {
|
|
116
|
+
return []
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const languagesInUse = languages.filter((l) => value.find((v) => v._key === l.id))
|
|
120
|
+
|
|
121
|
+
return value
|
|
122
|
+
.map((v, vIndex) => (vIndex === languagesInUse.findIndex((l) => l.id === v._key) ? null : v))
|
|
123
|
+
.filter(Boolean)
|
|
124
|
+
}, [value, languages])
|
|
125
|
+
|
|
126
|
+
const languagesAreValid = useMemo(
|
|
127
|
+
() => languages?.length && languages.every((item) => item.id && item.title),
|
|
128
|
+
[languages]
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if (!languagesAreValid) {
|
|
132
|
+
return <Feedback />
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<Stack space={2}>
|
|
137
|
+
{members?.length > 0 ? (
|
|
138
|
+
<Table>
|
|
139
|
+
<tbody>
|
|
140
|
+
{members.map((member) => (
|
|
141
|
+
<TableRow
|
|
142
|
+
key={member.key}
|
|
143
|
+
tone={
|
|
144
|
+
member?.item?.validation?.length > 0
|
|
145
|
+
? getToneFromValidation(member.item.validation)
|
|
146
|
+
: undefined
|
|
147
|
+
}
|
|
148
|
+
>
|
|
149
|
+
<TableCell style={{verticalAlign: 'bottom'}}>
|
|
150
|
+
<Box paddingY={3} paddingRight={2}>
|
|
151
|
+
<Label muted size={1}>
|
|
152
|
+
{member.key}
|
|
153
|
+
</Label>
|
|
154
|
+
</Box>
|
|
155
|
+
</TableCell>
|
|
156
|
+
<TableCell paddingRight={2} style={{width: `100%`}}>
|
|
157
|
+
<MemberItem {...props} member={member} />
|
|
158
|
+
</TableCell>
|
|
159
|
+
<TableCell style={{verticalAlign: 'bottom'}}>
|
|
160
|
+
<Flex align="center" justify="flex-end" gap={3}>
|
|
161
|
+
{/* Possibly unncessary, validation shows up in <MemberItem /> */}
|
|
162
|
+
{member.item.validation.length > 0 ? (
|
|
163
|
+
<Box paddingLeft={2}>
|
|
164
|
+
<FormFieldValidationStatus validation={member.item.validation} />
|
|
165
|
+
</Box>
|
|
166
|
+
) : null}
|
|
167
|
+
<Button
|
|
168
|
+
mode="ghost"
|
|
169
|
+
icon={RemoveIcon}
|
|
170
|
+
tone="critical"
|
|
171
|
+
disabled={typeof readOnly === 'boolean' ? readOnly : false}
|
|
172
|
+
onClick={() => handleUnsetByKey(member.key)}
|
|
173
|
+
/>
|
|
174
|
+
</Flex>
|
|
175
|
+
</TableCell>
|
|
176
|
+
</TableRow>
|
|
177
|
+
))}
|
|
178
|
+
</tbody>
|
|
179
|
+
</Table>
|
|
180
|
+
) : null}
|
|
181
|
+
|
|
182
|
+
{languagesOutOfOrder.length > 0 && allKeysAreLanguages ? (
|
|
183
|
+
<Button
|
|
184
|
+
tone="caution"
|
|
185
|
+
icon={RestoreIcon}
|
|
186
|
+
onClick={() => handleRestoreOrder()}
|
|
187
|
+
text="Restore order of languages"
|
|
188
|
+
/>
|
|
189
|
+
) : null}
|
|
190
|
+
|
|
191
|
+
{value && value.length < languages.length ? (
|
|
192
|
+
<Stack space={2}>
|
|
193
|
+
{/* No more than 5 columns */}
|
|
194
|
+
<Grid columns={Math.min(languages.length, 5)} gap={2}>
|
|
195
|
+
{languages.map((language) => (
|
|
196
|
+
<Button
|
|
197
|
+
key={language.id}
|
|
198
|
+
tone="primary"
|
|
199
|
+
mode="ghost"
|
|
200
|
+
fontSize={1}
|
|
201
|
+
disabled={readOnly || Boolean(value?.find((item) => item._key === language.id))}
|
|
202
|
+
text={language.id.toUpperCase()}
|
|
203
|
+
icon={AddIcon}
|
|
204
|
+
onClick={() => handleAddLanguage(language.id)}
|
|
205
|
+
/>
|
|
206
|
+
))}
|
|
207
|
+
</Grid>
|
|
208
|
+
<Button
|
|
209
|
+
tone="primary"
|
|
210
|
+
mode="ghost"
|
|
211
|
+
disabled={readOnly || (value && value?.length >= languages?.length)}
|
|
212
|
+
icon={AddIcon}
|
|
213
|
+
text={value?.length ? `Add missing languages` : `Add all languages`}
|
|
214
|
+
onClick={() => handleAddLanguage()}
|
|
215
|
+
/>
|
|
216
|
+
</Stack>
|
|
217
|
+
) : null}
|
|
218
|
+
</Stack>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {NodeValidation} from 'sanity/form'
|
|
2
|
+
import {CardTone} from '@sanity/ui'
|
|
3
|
+
|
|
4
|
+
export function getToneFromValidation(validations: NodeValidation[]): CardTone {
|
|
5
|
+
if (!validations.length) {
|
|
6
|
+
return `default`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const validationLevels = validations.map((v) => v.level)
|
|
10
|
+
|
|
11
|
+
if (validationLevels.includes('error')) {
|
|
12
|
+
return `critical`
|
|
13
|
+
} else if (validationLevels.includes('warning')) {
|
|
14
|
+
return `caution`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return `default`
|
|
18
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export * from './internationalizedArray'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Because of the complexity of the field, there's no utility currently to using the plugin framework
|
|
5
|
+
* We need to register:
|
|
6
|
+
* - an array field which
|
|
7
|
+
* - only has a single object field with
|
|
8
|
+
* - a single inner field and
|
|
9
|
+
* - an array of languages
|
|
10
|
+
* ...this is easier with a helper function
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// import React from 'react'
|
|
14
|
+
// import {createPlugin} from 'sanity'
|
|
15
|
+
|
|
16
|
+
// import InternationalizedArrayInput from './components/InternationalizedArrayInput'
|
|
17
|
+
// import {PluginConfig} from './types'
|
|
18
|
+
|
|
19
|
+
// const CONFIG_DEFAULT = {languages: []}
|
|
20
|
+
|
|
21
|
+
// export const internationalizedArray = createPlugin<PluginConfig>((config = CONFIG_DEFAULT) => {
|
|
22
|
+
// return {
|
|
23
|
+
// name: 'sanity-plugin-internationalized-array',
|
|
24
|
+
// form: {
|
|
25
|
+
// renderInput: (inputProps: unknown, next: unknown) => {
|
|
26
|
+
// if (
|
|
27
|
+
// config.languages.length &&
|
|
28
|
+
// inputProps?.schemaType?.jsonType === 'array' &&
|
|
29
|
+
// inputProps?.schemaType?.options.i18n === true
|
|
30
|
+
// ) {
|
|
31
|
+
// if (inputProps.schemaType.of.length > 1) {
|
|
32
|
+
// return <div>Cannot have more than one field type in the array</div>
|
|
33
|
+
// }
|
|
34
|
+
|
|
35
|
+
// if (inputProps.schemaType.of[0].jsonType !== 'object') {
|
|
36
|
+
// return <div>Single Field in the Array must be an object</div>
|
|
37
|
+
// }
|
|
38
|
+
|
|
39
|
+
// if (inputProps.schemaType.of[0].fields[0].name !== 'value') {
|
|
40
|
+
// return <div>Single Field in the Object must be named `value`</div>
|
|
41
|
+
// }
|
|
42
|
+
|
|
43
|
+
// console.log({inputProps})
|
|
44
|
+
// return <InternationalizedArrayInput inputProps={inputProps} {...config} />
|
|
45
|
+
// }
|
|
46
|
+
|
|
47
|
+
// return null
|
|
48
|
+
// },
|
|
49
|
+
// },
|
|
50
|
+
// }
|
|
51
|
+
// })
|
|
@@ -1,31 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
CustomValidatorResult,
|
|
3
|
+
defineField,
|
|
4
|
+
FieldDefinition,
|
|
5
|
+
Rule,
|
|
6
|
+
SchemaType,
|
|
7
|
+
ValidationError,
|
|
8
|
+
} from 'sanity'
|
|
9
|
+
|
|
10
|
+
import InternationalizedArrayInput from './components/InternationalizedArrayInput'
|
|
11
|
+
import {AllowedType, ArrayConfig, Language, Value} from './types'
|
|
12
|
+
|
|
13
|
+
const CONFIG_DEFAULT = {name: `title`, type: `string` as AllowedType, languages: []}
|
|
14
|
+
|
|
15
|
+
export function internationalizedArray(config: ArrayConfig = CONFIG_DEFAULT): FieldDefinition {
|
|
16
|
+
const {name, type, languages} = config
|
|
3
17
|
|
|
4
|
-
export function internationalizedArray(config: ArrayConfig) {
|
|
5
|
-
const {name = `title`, type = `string`, languages = [], showNativeInput = false} = config
|
|
6
18
|
const configValidation = Array.isArray(config?.validation)
|
|
7
19
|
? config.validation
|
|
8
20
|
: [config?.validation]
|
|
9
21
|
|
|
10
|
-
return {
|
|
22
|
+
return defineField({
|
|
11
23
|
name,
|
|
12
24
|
title: config?.title ?? undefined,
|
|
13
25
|
group: config?.group ?? undefined,
|
|
14
26
|
hidden: config?.hidden ?? undefined,
|
|
15
27
|
readOnly: config?.readOnly ?? undefined,
|
|
16
28
|
type: 'array',
|
|
17
|
-
|
|
18
|
-
options: {
|
|
19
|
-
languages,
|
|
20
|
-
showNativeInput,
|
|
21
|
-
},
|
|
29
|
+
components: {input: InternationalizedArrayInput},
|
|
30
|
+
options: {languages},
|
|
22
31
|
of: [
|
|
23
32
|
{
|
|
24
33
|
type: 'object',
|
|
25
|
-
fields: [
|
|
34
|
+
fields: [
|
|
35
|
+
{
|
|
36
|
+
name: 'value',
|
|
37
|
+
type,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
26
40
|
preview: {
|
|
27
41
|
select: {title: 'value', key: '_key'},
|
|
28
|
-
prepare(
|
|
42
|
+
prepare(select) {
|
|
43
|
+
const {title, key} = select as Record<string, string>
|
|
44
|
+
|
|
29
45
|
return {
|
|
30
46
|
title,
|
|
31
47
|
subtitle: key.toUpperCase(),
|
|
@@ -34,51 +50,62 @@ export function internationalizedArray(config: ArrayConfig) {
|
|
|
34
50
|
},
|
|
35
51
|
},
|
|
36
52
|
],
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const nonLanguageKeys = value?.length
|
|
42
|
-
? value.filter((item) => !languages.find((language) => item._key === language.id))
|
|
43
|
-
: []
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
validation: (rule: Rule) => {
|
|
55
|
+
const rules = [] as Rule[]
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
rules.push(
|
|
58
|
+
rule.custom<Value[]>((value, context) => {
|
|
59
|
+
const {languages: contextLanguages}: {languages: Language[]} =
|
|
60
|
+
context?.type?.options ?? {}
|
|
61
|
+
const nonLanguageKeys = value?.length
|
|
62
|
+
? value.filter(
|
|
63
|
+
(item) => !contextLanguages.find((language) => item._key === language.id)
|
|
64
|
+
)
|
|
65
|
+
: []
|
|
66
|
+
if (nonLanguageKeys.length) {
|
|
67
|
+
return {
|
|
68
|
+
message: `Array item keys must be valid languages registered to the field type`,
|
|
69
|
+
paths: nonLanguageKeys.map((item) => [{_key: item._key}]),
|
|
70
|
+
}
|
|
49
71
|
}
|
|
50
|
-
}
|
|
51
72
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.reduce((acc, cur) => {
|
|
57
|
-
if (acc[cur._key]) {
|
|
58
|
-
return {...acc, [cur._key]: [...acc[cur._key], cur]}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
...acc,
|
|
63
|
-
[cur._key]: [cur],
|
|
64
|
-
}
|
|
65
|
-
}, {})
|
|
66
|
-
: {}
|
|
67
|
-
|
|
68
|
-
const duplicateValues = Object.values(valuesByLanguage)
|
|
69
|
-
.filter((item) => item?.length > 1)
|
|
70
|
-
.flat()
|
|
73
|
+
// Ensure there's no duplicate `language` fields
|
|
74
|
+
type KeyedValues = {
|
|
75
|
+
[key: string]: Value[]
|
|
76
|
+
}
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
const valuesByLanguage = value?.length
|
|
79
|
+
? value
|
|
80
|
+
.filter((item) => Boolean(item?._key))
|
|
81
|
+
.reduce((acc, cur) => {
|
|
82
|
+
if (acc[cur._key]) {
|
|
83
|
+
return {...acc, [cur._key]: [...acc[cur._key], cur]}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
...acc,
|
|
87
|
+
[cur._key]: [cur],
|
|
88
|
+
}
|
|
89
|
+
}, {} as KeyedValues)
|
|
90
|
+
: {}
|
|
91
|
+
const duplicateValues = Object.values(valuesByLanguage)
|
|
92
|
+
.filter((item) => item?.length > 1)
|
|
93
|
+
.flat()
|
|
94
|
+
if (duplicateValues.length) {
|
|
95
|
+
return {
|
|
96
|
+
message: 'There can only be one field per language',
|
|
97
|
+
paths: duplicateValues.map((item) => [{_key: item._key}]),
|
|
98
|
+
}
|
|
76
99
|
}
|
|
77
|
-
|
|
100
|
+
return true
|
|
101
|
+
})
|
|
102
|
+
)
|
|
78
103
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
104
|
+
if (languages?.length) {
|
|
105
|
+
rules.push(rule.max(languages.length))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return [...rules, ...configValidation].filter(Boolean)
|
|
109
|
+
},
|
|
110
|
+
})
|
|
84
111
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import {Rule} from '
|
|
1
|
+
import {Rule, ArraySchemaType} from 'sanity'
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type Language = {
|
|
4
|
+
id: string
|
|
5
|
+
title: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type AllowedType = 'string' | 'number' | 'boolean' | 'text'
|
|
9
|
+
|
|
10
|
+
export type ArrayConfig = {
|
|
4
11
|
name: string
|
|
5
|
-
type:
|
|
12
|
+
type: AllowedType
|
|
13
|
+
languages: Language[]
|
|
6
14
|
title?: string
|
|
7
15
|
group?: string
|
|
8
16
|
hidden?: boolean | (() => boolean)
|
|
@@ -15,12 +23,12 @@ export type Value = {
|
|
|
15
23
|
value?: string
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
export type
|
|
19
|
-
|
|
20
|
-
title: string
|
|
26
|
+
export type PluginConfig = {
|
|
27
|
+
languages: Language[]
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
export type
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
export type ArraySchemaWithLanguageOptions = ArraySchemaType & {
|
|
31
|
+
options: {
|
|
32
|
+
languages: Language[]
|
|
33
|
+
}
|
|
26
34
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
|
|
2
|
+
const {name, version, sanityExchangeUrl} = require('./package.json')
|
|
3
|
+
|
|
4
|
+
export default showIncompatiblePluginDialog({
|
|
5
|
+
name: name,
|
|
6
|
+
versions: {
|
|
7
|
+
v3: version,
|
|
8
|
+
v2: undefined,
|
|
9
|
+
},
|
|
10
|
+
sanityExchangeUrl,
|
|
11
|
+
})
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.Table = Table;
|
|
7
|
-
exports.TableCell = TableCell;
|
|
8
|
-
exports.TableRow = TableRow;
|
|
9
|
-
|
|
10
|
-
var _react = _interopRequireDefault(require("react"));
|
|
11
|
-
|
|
12
|
-
var _styledComponents = _interopRequireWildcard(require("styled-components"));
|
|
13
|
-
|
|
14
|
-
var _ui = require("@sanity/ui");
|
|
15
|
-
|
|
16
|
-
var _excluded = ["children"],
|
|
17
|
-
_excluded2 = ["children"],
|
|
18
|
-
_excluded3 = ["children"];
|
|
19
|
-
|
|
20
|
-
var _templateObject, _templateObject2, _templateObject3;
|
|
21
|
-
|
|
22
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
23
|
-
|
|
24
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
25
|
-
|
|
26
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
27
|
-
|
|
28
|
-
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
29
|
-
|
|
30
|
-
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
31
|
-
|
|
32
|
-
function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
|
|
33
|
-
|
|
34
|
-
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
35
|
-
|
|
36
|
-
// Wrappers required because of bug with passing down "as" prop
|
|
37
|
-
// https://github.com/styled-components/styled-components/issues/2449
|
|
38
|
-
// Table
|
|
39
|
-
var TableWrapper = function TableWrapper() {
|
|
40
|
-
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
41
|
-
return /*#__PURE__*/_react.default.createElement(_ui.Box, _extends({
|
|
42
|
-
as: "table"
|
|
43
|
-
}, props));
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
var StyledTable = (0, _styledComponents.default)(TableWrapper)(() => (0, _styledComponents.css)(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n display: table;\n width: 100%;\n\n &:not([hidden]) {\n display: table;\n }\n "]))));
|
|
47
|
-
|
|
48
|
-
function Table(props) {
|
|
49
|
-
var children = props.children,
|
|
50
|
-
rest = _objectWithoutProperties(props, _excluded);
|
|
51
|
-
|
|
52
|
-
return /*#__PURE__*/_react.default.createElement(StyledTable, rest, children);
|
|
53
|
-
} // Row
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
var RowWrapper = function RowWrapper() {
|
|
57
|
-
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
58
|
-
return /*#__PURE__*/_react.default.createElement(_ui.Card, _extends({
|
|
59
|
-
as: "tr"
|
|
60
|
-
}, props));
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
var StyledRow = (0, _styledComponents.default)(RowWrapper)(() => (0, _styledComponents.css)(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n display: table-row;\n\n &:not([hidden]) {\n display: table-row;\n }\n "]))));
|
|
64
|
-
|
|
65
|
-
function TableRow(props) {
|
|
66
|
-
var children = props.children,
|
|
67
|
-
rest = _objectWithoutProperties(props, _excluded2);
|
|
68
|
-
|
|
69
|
-
return /*#__PURE__*/_react.default.createElement(StyledRow, rest, children);
|
|
70
|
-
} // Cell
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
var CellWrapper = function CellWrapper() {
|
|
74
|
-
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
75
|
-
return /*#__PURE__*/_react.default.createElement(_ui.Box, _extends({
|
|
76
|
-
as: "td"
|
|
77
|
-
}, props));
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
var StyledCell = (0, _styledComponents.default)(CellWrapper)(() => (0, _styledComponents.css)(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n display: table-cell;\n\n &:not([hidden]) {\n display: table-cell;\n }\n "]))));
|
|
81
|
-
|
|
82
|
-
function TableCell(props) {
|
|
83
|
-
var children = props.children,
|
|
84
|
-
rest = _objectWithoutProperties(props, _excluded3);
|
|
85
|
-
|
|
86
|
-
return /*#__PURE__*/_react.default.createElement(StyledCell, rest, children);
|
|
87
|
-
}
|
|
88
|
-
//# sourceMappingURL=Table.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Table.js","names":["TableWrapper","props","StyledTable","styled","css","Table","children","rest","RowWrapper","StyledRow","TableRow","CellWrapper","StyledCell","TableCell"],"sources":["../../src/LanguageArray/Table.tsx"],"sourcesContent":["import React from 'react'\nimport styled, {css} from 'styled-components'\nimport {Box, BoxProps, Card, CardProps} from '@sanity/ui'\n\n// Wrappers required because of bug with passing down \"as\" prop\n// https://github.com/styled-components/styled-components/issues/2449\n\n// Table\nconst TableWrapper = (props = {}) => {\n return <Box as=\"table\" {...props} />\n}\n\nconst StyledTable = styled(TableWrapper)(\n () =>\n css`\n display: table;\n width: 100%;\n\n &:not([hidden]) {\n display: table;\n }\n `\n)\n\ntype TableProps = BoxProps & {\n children: React.ReactNode\n style?: React.CSSProperties\n}\n\nexport function Table(props: TableProps) {\n const {children, ...rest} = props\n\n return <StyledTable {...rest}>{children}</StyledTable>\n}\n\n// Row\nconst RowWrapper = (props = {}) => {\n return <Card as=\"tr\" {...props} />\n}\n\nconst StyledRow = styled(RowWrapper)(\n () =>\n css`\n display: table-row;\n\n &:not([hidden]) {\n display: table-row;\n }\n `\n)\n\ntype TableRowProps = CardProps & {\n children: React.ReactNode\n style?: React.CSSProperties\n}\n\nexport function TableRow(props: TableRowProps) {\n const {children, ...rest} = props\n\n return <StyledRow {...rest}>{children}</StyledRow>\n}\n\n// Cell\nconst CellWrapper = (props = {}) => {\n return <Box as=\"td\" {...props} />\n}\n\nconst StyledCell = styled(CellWrapper)(\n () =>\n css`\n display: table-cell;\n\n &:not([hidden]) {\n display: table-cell;\n }\n `\n)\n\ntype TableCellProps = BoxProps & {\n children: React.ReactNode\n style?: React.CSSProperties\n}\n\nexport function TableCell(props: TableCellProps) {\n const {children, ...rest} = props\n\n return <StyledCell {...rest}>{children}</StyledCell>\n}\n"],"mappings":";;;;;;;;;AAAA;;AACA;;AACA;;;;;;;;;;;;;;;;;;;;;;AAEA;AACA;AAEA;AACA,IAAMA,YAAY,GAAG,SAAfA,YAAe,GAAgB;EAAA,IAAfC,KAAe,uEAAP,EAAO;EACnC,oBAAO,6BAAC,OAAD;IAAK,EAAE,EAAC;EAAR,GAAoBA,KAApB,EAAP;AACD,CAFD;;AAIA,IAAMC,WAAW,GAAG,IAAAC,yBAAA,EAAOH,YAAP,EAClB,UACEI,qBADF,oLADkB,CAApB;;AAiBO,SAASC,KAAT,CAAeJ,KAAf,EAAkC;EACvC,IAAOK,QAAP,GAA4BL,KAA5B,CAAOK,QAAP;EAAA,IAAoBC,IAApB,4BAA4BN,KAA5B;;EAEA,oBAAO,6BAAC,WAAD,EAAiBM,IAAjB,EAAwBD,QAAxB,CAAP;AACD,C,CAED;;;AACA,IAAME,UAAU,GAAG,SAAbA,UAAa,GAAgB;EAAA,IAAfP,KAAe,uEAAP,EAAO;EACjC,oBAAO,6BAAC,QAAD;IAAM,EAAE,EAAC;EAAT,GAAkBA,KAAlB,EAAP;AACD,CAFD;;AAIA,IAAMQ,SAAS,GAAG,IAAAN,yBAAA,EAAOK,UAAP,EAChB,UACEJ,qBADF,0KADgB,CAAlB;;AAgBO,SAASM,QAAT,CAAkBT,KAAlB,EAAwC;EAC7C,IAAOK,QAAP,GAA4BL,KAA5B,CAAOK,QAAP;EAAA,IAAoBC,IAApB,4BAA4BN,KAA5B;;EAEA,oBAAO,6BAAC,SAAD,EAAeM,IAAf,EAAsBD,QAAtB,CAAP;AACD,C,CAED;;;AACA,IAAMK,WAAW,GAAG,SAAdA,WAAc,GAAgB;EAAA,IAAfV,KAAe,uEAAP,EAAO;EAClC,oBAAO,6BAAC,OAAD;IAAK,EAAE,EAAC;EAAR,GAAiBA,KAAjB,EAAP;AACD,CAFD;;AAIA,IAAMW,UAAU,GAAG,IAAAT,yBAAA,EAAOQ,WAAP,EACjB,UACEP,qBADF,4KADiB,CAAnB;;AAgBO,SAASS,SAAT,CAAmBZ,KAAnB,EAA0C;EAC/C,IAAOK,QAAP,GAA4BL,KAA5B,CAAOK,QAAP;EAAA,IAAoBC,IAApB,4BAA4BN,KAA5B;;EAEA,oBAAO,6BAAC,UAAD,EAAgBM,IAAhB,EAAuBD,QAAvB,CAAP;AACD"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = ValueInput;
|
|
7
|
-
|
|
8
|
-
var _react = _interopRequireDefault(require("react"));
|
|
9
|
-
|
|
10
|
-
var _FormBuilderInput = require("@sanity/form-builder/lib/FormBuilderInput");
|
|
11
|
-
|
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
-
|
|
14
|
-
function ValueInput(props) {
|
|
15
|
-
return /*#__PURE__*/_react.default.createElement(_FormBuilderInput.FormBuilderInput, props);
|
|
16
|
-
}
|
|
17
|
-
//# sourceMappingURL=ValueInput.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ValueInput.js","names":["ValueInput","props"],"sources":["../../src/LanguageArray/ValueInput.tsx"],"sourcesContent":["import React from 'react'\nimport {FormBuilderInput} from '@sanity/form-builder/lib/FormBuilderInput'\n\nexport default function ValueInput(props) {\n return <FormBuilderInput {...props} />\n}\n"],"mappings":";;;;;;;AAAA;;AACA;;;;AAEe,SAASA,UAAT,CAAoBC,KAApB,EAA2B;EACxC,oBAAO,6BAAC,kCAAD,EAAsBA,KAAtB,CAAP;AACD"}
|