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.
Files changed (37) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +19 -15
  3. package/lib/cjs/index.js +468 -0
  4. package/lib/cjs/index.js.map +1 -0
  5. package/lib/esm/index.js +449 -0
  6. package/lib/esm/index.js.map +1 -0
  7. package/lib/types/index.d.ts +19 -0
  8. package/lib/types/index.d.ts.map +1 -0
  9. package/package.json +47 -34
  10. package/sanity.json +7 -6
  11. package/src/components/Feedback.tsx +27 -0
  12. package/src/components/InternationalizedArrayInput.tsx +220 -0
  13. package/src/{LanguageArray → components}/Table.tsx +0 -0
  14. package/src/components/getToneFromValidation.ts +18 -0
  15. package/src/index.tsx +51 -0
  16. package/src/internationalizedArray.ts +80 -53
  17. package/src/types.ts +17 -9
  18. package/v2-incompatible.js +11 -0
  19. package/lib/LanguageArray/Table.js +0 -88
  20. package/lib/LanguageArray/Table.js.map +0 -1
  21. package/lib/LanguageArray/ValueInput.js +0 -17
  22. package/lib/LanguageArray/ValueInput.js.map +0 -1
  23. package/lib/LanguageArray/index.js +0 -253
  24. package/lib/LanguageArray/index.js.map +0 -1
  25. package/lib/hooks/useUnsetInputComponent.js +0 -32
  26. package/lib/hooks/useUnsetInputComponent.js.map +0 -1
  27. package/lib/index.js +0 -13
  28. package/lib/index.js.map +0 -1
  29. package/lib/internationalizedArray.js +0 -105
  30. package/lib/internationalizedArray.js.map +0 -1
  31. package/lib/types.js +0 -2
  32. package/lib/types.js.map +0 -1
  33. package/migrations/transformObjectToArray.js +0 -94
  34. package/src/LanguageArray/ValueInput.tsx +0 -6
  35. package/src/LanguageArray/index.tsx +0 -311
  36. package/src/hooks/useUnsetInputComponent.tsx +0 -17
  37. package/src/index.ts +0 -3
@@ -1,311 +0,0 @@
1
- import React, {forwardRef, useCallback, useMemo} from 'react'
2
- import {Code, Text, Card, Label, Box, Stack, Button, Grid, Flex} from '@sanity/ui'
3
- import {withDocument} from 'part:@sanity/form-builder'
4
- import {PatchEvent, setIfMissing, insert, unset, set} from '@sanity/form-builder/PatchEvent'
5
- import {AddIcon, RemoveIcon, RestoreIcon} from '@sanity/icons'
6
- import {FormFieldValidationStatus} from '@sanity/base/components'
7
- import {FieldPresence} from '@sanity/base/presence'
8
- import {FormBuilderInput} from '@sanity/form-builder/lib/FormBuilderInput'
9
-
10
- import ValueInput from './ValueInput'
11
- import {Table, TableCell, TableRow} from './Table'
12
- import {useUnsetInputComponent} from '../hooks/useUnsetInputComponent'
13
-
14
- const schemaExample = {
15
- name: 'title',
16
- type: 'localisedArray',
17
- options: {
18
- languages: [
19
- {id: 'en', title: 'English'},
20
- {id: 'no', title: 'Norsk'},
21
- ],
22
- },
23
- }
24
-
25
- type Value = {
26
- _key: string
27
- value?: string
28
- }
29
-
30
- type Language = {
31
- id: string
32
- title: string
33
- }
34
-
35
- type Options = {
36
- languages: Language[]
37
- showNativeInput: boolean
38
- }
39
-
40
- const DEFAULT_OPTIONS = {
41
- languages: [],
42
- showNativeInput: false,
43
- }
44
-
45
- const LanguageArrayWrapper = forwardRef(function CustomComponent(props, ref) {
46
- const {onChange, onBlur, readOnly, presence, markers} = props
47
- const value: Value[] = props?.value
48
-
49
- // IMPORTANT: leaving out will cause the browser to lock up in an infinite loop
50
- const type = useUnsetInputComponent(props.type)
51
- const options: Options = type?.options ?? DEFAULT_OPTIONS
52
- const {languages, showNativeInput} = options
53
-
54
- const handleAddLanguage = useCallback(
55
- (languageId?: string) => {
56
- // Create new items
57
- const newItems = languageId
58
- ? // Just one for this language
59
- [{_key: languageId}]
60
- : // Or one for every missing language
61
- languages
62
- .filter((language) =>
63
- value?.length ? !value.find((v) => v._key === language.id) : true
64
- )
65
- .map((language) => ({_key: language.id}))
66
-
67
- // Insert new items in the correct order
68
- const languagesInUse = value?.length ? value.map((v) => v) : []
69
-
70
- const insertions = newItems.map((item) => {
71
- // What's the original index of this language?
72
- const languageIndex = languages.findIndex((l) => item._key === l.id)
73
-
74
- // What languages are there beyond that index?
75
- const remainingLanguages = languages.slice(languageIndex + 1)
76
-
77
- // So what is the index in the current value array of the next language in the language array?
78
- const nextLanguageIndex = languagesInUse.findIndex((l) =>
79
- remainingLanguages.find((r) => r.id === l._key)
80
- )
81
-
82
- // Keep local state up to date incase multiple insertions are being made
83
- if (nextLanguageIndex < 0) {
84
- languagesInUse.push(item)
85
- } else {
86
- languagesInUse.splice(nextLanguageIndex, 0, item)
87
- }
88
-
89
- return nextLanguageIndex < 0
90
- ? // No next language (-1), add to end of array
91
- insert([item], 'after', [nextLanguageIndex])
92
- : // Next language found, insert before that
93
- insert([item], 'before', [nextLanguageIndex])
94
- })
95
-
96
- onChange(PatchEvent.from(setIfMissing([]), ...insertions))
97
- },
98
- [languages, onChange, value]
99
- )
100
-
101
- const handleUnsetByKey = useCallback(
102
- (_key) => {
103
- onChange(PatchEvent.from(unset([{_key}])))
104
- },
105
- [onChange]
106
- )
107
-
108
- const handleInnerValueChange = useCallback(
109
- (patchEvent: PatchEvent, _key: string) => {
110
- const inputValue = patchEvent.patches[0]?.value
111
- const inputPath = [{_key}, `value`]
112
-
113
- onChange(PatchEvent.from(inputValue ? set(inputValue, inputPath) : unset(inputPath)))
114
- },
115
- [onChange]
116
- )
117
-
118
- // TODO: This is lazy, reordering and re-setting the whole array – it should be surgical
119
- const handleRestoreOrder = useCallback(() => {
120
- // Create a new value array in the correct order
121
- const updatedValue = value.reduce((acc, v) => {
122
- const newIndex = languages.findIndex((l) => l.id === v._key)
123
-
124
- acc[newIndex] = v
125
-
126
- return acc
127
- }, [])
128
-
129
- onChange(PatchEvent.from(unset(), set(updatedValue)))
130
- }, [languages, onChange, value])
131
-
132
- // Check languages are in the correct order
133
- const languagesOutOfOrder = useMemo(() => {
134
- if (!value?.length) {
135
- return []
136
- }
137
-
138
- const languagesInUse = languages.filter((l) => value.find((v) => v._key === l.id))
139
-
140
- return value
141
- .map((v, vIndex) => (vIndex === languagesInUse.findIndex((l) => l.id === v._key) ? null : v))
142
- .filter(Boolean)
143
- }, [value, languages])
144
-
145
- // Check options are supplied and valid
146
- const languagesAreValid = useMemo(
147
- () => languages?.length && languages.every((item) => item.id && item.title),
148
- [languages]
149
- )
150
-
151
- if (!languagesAreValid) {
152
- return (
153
- <Card tone="caution" border radius={2} padding={3}>
154
- <Stack space={4}>
155
- <Text>
156
- An array of language objects must be passed into the <code>{type.name}</code> field as
157
- options, each with an <code>id</code> and <code>title</code> field. Example:
158
- </Text>
159
- <Card padding={2} border radius={2}>
160
- <Code size={1} language="javascript">
161
- {JSON.stringify(schemaExample, null, 2)}
162
- </Code>
163
- </Card>
164
- </Stack>
165
- </Card>
166
- )
167
- }
168
-
169
- const validationMarkers = markers?.length
170
- ? markers.filter((mark) => mark.type === `validation`)
171
- : []
172
- const invalidKeys = validationMarkers
173
- .map((mark) => mark.path)
174
- .flat()
175
- .map((item) => item._key)
176
-
177
- return (
178
- <Stack space={3}>
179
- <Box>
180
- <Text size={1} weight="bold">
181
- {type?.title ?? type.name}
182
- </Text>
183
- </Box>
184
- {/* Loop over the values */}
185
- {value?.length > 0 ? (
186
- <Card>
187
- <Table>
188
- <tbody>
189
- {value.map((item) => (
190
- <TableRow
191
- key={item._key}
192
- tone={
193
- // TODO: Move this logic somewhere else
194
- invalidKeys.includes(item._key)
195
- ? `critical`
196
- : undefined || languagesOutOfOrder.find((l) => l._key === item._key)
197
- ? `caution`
198
- : undefined
199
- }
200
- >
201
- {/* To render each individual field in this type */}
202
- {type?.of?.length > 0 &&
203
- type?.of.map((subType) => (
204
- <>
205
- {subType?.fields?.length > 0 ? (
206
- <>
207
- <TableCell>
208
- <Box paddingRight={2}>
209
- <Label muted size={1}>
210
- {item._key}
211
- </Label>
212
- </Box>
213
- </TableCell>
214
- <TableCell paddingRight={2} style={{width: `100%`}}>
215
- {/* There _should_ only be one field */}
216
- {subType.fields.map((subTypeField) => (
217
- <ValueInput
218
- key={subTypeField.name}
219
- onChange={(patchEvent) =>
220
- handleInnerValueChange(patchEvent, item._key)
221
- }
222
- onBlur={onBlur}
223
- // We don't want the array item to open onFocus
224
- onFocus={() => null}
225
- path={[{_key: item._key}, subTypeField.name]}
226
- // focusPath={[{_key: item._key}, subTypeField.name]}
227
- parent={item}
228
- readOnly={readOnly}
229
- type={subTypeField}
230
- value={item.value}
231
- level={props.level + 1}
232
- markers={[]}
233
- compareValue={props.compareValue}
234
- />
235
- ))}
236
- </TableCell>
237
- </>
238
- ) : null}
239
- </>
240
- ))}
241
-
242
- <TableCell>
243
- <Flex align="center" justify="flex-end" gap={3}>
244
- {presence?.length > 0 ? (
245
- <FieldPresence maxAvatars={1} presence={presence} />
246
- ) : null}
247
- {invalidKeys.includes(item._key) ? (
248
- <Box paddingLeft={2}>
249
- <FormFieldValidationStatus __unstable_markers={validationMarkers} />
250
- </Box>
251
- ) : null}
252
- <Button
253
- mode="ghost"
254
- icon={RemoveIcon}
255
- tone="critical"
256
- disabled={readOnly}
257
- onClick={() => handleUnsetByKey(item._key)}
258
- />
259
- </Flex>
260
- </TableCell>
261
- </TableRow>
262
- ))}
263
- </tbody>
264
- </Table>
265
- </Card>
266
- ) : null}
267
-
268
- {languagesOutOfOrder.length > 0 ? (
269
- <Button
270
- tone="caution"
271
- disabled={languagesOutOfOrder.length > languages.length}
272
- icon={RestoreIcon}
273
- onClick={() => handleRestoreOrder()}
274
- text="Restore order of languages"
275
- />
276
- ) : null}
277
-
278
- {languages.length > 0 ? (
279
- <Stack space={2}>
280
- {/* No more than 5 columns */}
281
- <Grid columns={Math.min(languages.length, 5)} gap={2}>
282
- {languages.map((language) => (
283
- <Button
284
- key={language.id}
285
- tone="primary"
286
- mode="ghost"
287
- fontSize={1}
288
- disabled={readOnly || value?.find((item) => item._key === language.id)}
289
- text={language.id.toUpperCase()}
290
- icon={AddIcon}
291
- onClick={() => handleAddLanguage(language.id)}
292
- />
293
- ))}
294
- </Grid>
295
- <Button
296
- tone="primary"
297
- mode="ghost"
298
- disabled={readOnly || value?.length >= languages?.length}
299
- icon={AddIcon}
300
- text={value?.length ? `Add missing languages` : `Add all languages`}
301
- onClick={() => handleAddLanguage()}
302
- />
303
- </Stack>
304
- ) : null}
305
-
306
- {showNativeInput ? <FormBuilderInput {...props} type={type} ref={ref} /> : null}
307
- </Stack>
308
- )
309
- })
310
-
311
- export default withDocument(LanguageArrayWrapper)
@@ -1,17 +0,0 @@
1
- import React, {ReactNode} from 'react'
2
-
3
- export function useUnsetInputComponent(type: unknown, component?: ReactNode) {
4
- return React.useMemo(() => unsetInputComponent(type, component), [type, component])
5
- }
6
-
7
- function unsetInputComponent(type, component) {
8
- const t = {
9
- ...type,
10
- inputComponent: type.inputComponent === component ? undefined : type.inputComponent,
11
- }
12
- const typeOfType = t.type ? unsetInputComponent(t.type, component) : undefined
13
- return {
14
- ...t,
15
- type: typeOfType,
16
- }
17
- }
package/src/index.ts DELETED
@@ -1,3 +0,0 @@
1
- import {internationalizedArray as helperFunction} from './internationalizedArray'
2
-
3
- export const internationalizedArray = (config) => helperFunction(config)