sanity-plugin-internationalized-array 2.1.0 → 3.0.0-canary.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/lib/index.mjs CHANGED
@@ -1,15 +1,15 @@
1
1
  import * as suspend from "suspend-react";
2
2
  import { suspend as suspend$1 } from "suspend-react";
3
3
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
4
- import { isSanityDocument, setIfMissing, insert, PatchEvent, useClient, useWorkspace, useFormBuilder, defineDocumentFieldAction, useFormValue, set, ArrayOfObjectsItem, MemberItemError, defineField, unset, isDocumentSchemaType, definePlugin, isObjectInputProps } from "sanity";
4
+ import { isSanityDocument, setIfMissing, insert, PatchEvent, useClient, useWorkspace, defineDocumentFieldAction, useFormValue, set, ArrayOfObjectsItem, MemberItemError, defineField, unset, isDocumentSchemaType, definePlugin, isObjectInputProps } from "sanity";
5
5
  import { useLanguageFilterStudioContext } from "@sanity/language-filter";
6
6
  import { Grid, Button, useToast, Stack, Box, Text, Card, Code, Label, MenuButton, Menu, MenuItem, Flex, Spinner } from "@sanity/ui";
7
7
  import equal from "fast-deep-equal";
8
- import { useCallback, createContext, useContext, useDeferredValue, useMemo, memo, useRef, useEffect, createElement } from "react";
8
+ import { memo, useCallback, createContext, useContext, useDeferredValue, useMemo, useRef, useEffect, createElement } from "react";
9
9
  import { useDocumentPane } from "sanity/structure";
10
10
  import { AddIcon, TranslateIcon, RemoveCircleIcon } from "@sanity/icons";
11
11
  import get from "lodash/get.js";
12
- const namespace = "sanity-plugin-internationalized-array", version = "v0", preload = (fn) => suspend.preload(() => fn(), [version, namespace]), clear = () => suspend.clear([version, namespace]), peek = (selectedValue) => suspend.peek([version, namespace, selectedValue]), MAX_COLUMNS = 7, CONFIG_DEFAULT = {
12
+ const namespace = "sanity-plugin-internationalized-array", version = "v1", preload = (fn) => suspend.preload(() => fn(), [version, namespace]), clear = () => suspend.clear([version, namespace]), peek = (selectedValue) => suspend.peek([version, namespace, selectedValue]), MAX_COLUMNS = 7, CONFIG_DEFAULT = {
13
13
  languages: [],
14
14
  select: {},
15
15
  defaultLanguages: [],
@@ -20,11 +20,10 @@ const namespace = "sanity-plugin-internationalized-array", version = "v0", prelo
20
20
  }, getDocumentsToTranslate = (value, rootPath = []) => {
21
21
  if (Array.isArray(value)) {
22
22
  const arrayRootPath = [...rootPath], internationalizedValues = value.filter((item) => {
23
- if (Array.isArray(item))
24
- return !1;
23
+ if (Array.isArray(item)) return !1;
25
24
  if (typeof item == "object") {
26
- const type = item == null ? void 0 : item._type;
27
- return (type == null ? void 0 : type.startsWith("internationalizedArray")) && (type == null ? void 0 : type.endsWith("Value"));
25
+ const type = item?._type;
26
+ return type?.startsWith("internationalizedArray") && type?.endsWith("Value");
28
27
  }
29
28
  return !1;
30
29
  });
@@ -55,7 +54,7 @@ function AddButtons(props) {
55
54
  tone: "primary",
56
55
  mode: "ghost",
57
56
  fontSize: 1,
58
- disabled: readOnly || !!(value != null && value.find((item) => item._key === language.id)),
57
+ disabled: readOnly || !!value?.find((item) => item._key === language.id),
59
58
  text: language.id.toUpperCase(),
60
59
  icon: languages.length > MAX_COLUMNS ? void 0 : AddIcon,
61
60
  value: language.id,
@@ -64,6 +63,7 @@ function AddButtons(props) {
64
63
  language.id
65
64
  )) }) : null;
66
65
  }
66
+ var AddButtons$1 = memo(AddButtons);
67
67
  function DocumentAddButtons(props) {
68
68
  const { filteredLanguages } = useInternationalizedArrayContext(), value = isSanityDocument(props.value) ? props.value : void 0, toast = useToast(), { onChange } = useDocumentPane(), documentsToTranslation = getDocumentsToTranslate(value, []), handleDocumentButtonClick = useCallback(
69
69
  async (event) => {
@@ -76,7 +76,7 @@ function DocumentAddButtons(props) {
76
76
  return;
77
77
  }
78
78
  const alreadyTranslated = documentsToTranslation.filter(
79
- (translation) => (translation == null ? void 0 : translation._key) === languageId
79
+ (translation) => translation?._key === languageId
80
80
  ), removeDuplicates = documentsToTranslation.reduce((filteredTranslations, translation) => alreadyTranslated.filter(
81
81
  (alreadyTranslation) => alreadyTranslation.pathString === translation.pathString
82
82
  ).length > 0 || filteredTranslations.filter(
@@ -111,7 +111,7 @@ function DocumentAddButtons(props) {
111
111
  return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
112
112
  /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: "Add translation to internationalized fields" }) }),
113
113
  /* @__PURE__ */ jsx(
114
- AddButtons,
114
+ AddButtons$1,
115
115
  {
116
116
  languages: filteredLanguages,
117
117
  readOnly: !1,
@@ -128,7 +128,7 @@ const getSelectedValue = (select, document) => {
128
128
  for (const [key, path] of Object.entries(selection)) {
129
129
  let value = get(document, path);
130
130
  Array.isArray(value) && (value = value.filter(
131
- (item) => typeof item == "object" ? (item == null ? void 0 : item._type) === "reference" && "_ref" in item : !0
131
+ (item) => typeof item == "object" ? item?._type === "reference" && "_ref" in item : !0
132
132
  )), selectedValue[key] = value;
133
133
  }
134
134
  return selectedValue;
@@ -141,7 +141,7 @@ function useInternationalizedArrayContext() {
141
141
  return useContext(InternationalizedArrayContext);
142
142
  }
143
143
  function InternationalizedArrayProvider(props) {
144
- const { internationalizedArray: internationalizedArray2 } = props, client = useClient({ apiVersion: internationalizedArray2.apiVersion }), workspace = useWorkspace(), { value: document } = useFormBuilder(), deferredDocument = useDeferredValue(document), selectedValue = useMemo(
144
+ const { internationalizedArray: internationalizedArray2 } = props, client = useClient({ apiVersion: internationalizedArray2.apiVersion }), workspace = useWorkspace(), { formState } = useDocumentPane(), deferredDocument = useDeferredValue(formState?.value), selectedValue = useMemo(
145
145
  () => getSelectedValue(internationalizedArray2.select, deferredDocument),
146
146
  [internationalizedArray2.select, deferredDocument]
147
147
  ), languages = Array.isArray(internationalizedArray2.languages) ? internationalizedArray2.languages : suspend$1(
@@ -154,21 +154,14 @@ function InternationalizedArrayProvider(props) {
154
154
  return typeof documentType == "string" && languageFilterOptions.documentTypes.includes(documentType) ? languages.filter(
155
155
  (language) => selectedLanguageIds.includes(language.id)
156
156
  ) : languages;
157
- }, [deferredDocument, languageFilterOptions, languages, selectedLanguageIds]), showDocumentButtons = internationalizedArray2.buttonLocations.includes("document");
158
- return /* @__PURE__ */ jsx(
159
- InternationalizedArrayContext.Provider,
160
- {
161
- value: {
162
- ...internationalizedArray2,
163
- languages,
164
- filteredLanguages
165
- },
166
- children: showDocumentButtons ? /* @__PURE__ */ jsxs(Stack, { space: 5, children: [
167
- /* @__PURE__ */ jsx(DocumentAddButtons, { value: props.value }),
168
- props.renderDefault(props)
169
- ] }) : props.renderDefault(props)
170
- }
157
+ }, [deferredDocument, languageFilterOptions, languages, selectedLanguageIds]), showDocumentButtons = internationalizedArray2.buttonLocations.includes("document"), context = useMemo(
158
+ () => ({ ...internationalizedArray2, languages, filteredLanguages }),
159
+ [filteredLanguages, internationalizedArray2, languages]
171
160
  );
161
+ return /* @__PURE__ */ jsx(InternationalizedArrayContext.Provider, { value: context, children: showDocumentButtons ? /* @__PURE__ */ jsxs(Stack, { space: 5, children: [
162
+ /* @__PURE__ */ jsx(DocumentAddButtons, { value: props.value }),
163
+ props.renderDefault(props)
164
+ ] }) : props.renderDefault(props) });
172
165
  }
173
166
  var Preload = memo(function(props) {
174
167
  const client = useClient({ apiVersion: props.apiVersion });
@@ -181,7 +174,7 @@ function checkAllLanguagesArePresent(languages, value) {
181
174
  return languagesInUseIds.length === filteredLanguageIds.length && languagesInUseIds.every((l) => filteredLanguageIds.includes(l));
182
175
  }
183
176
  function createAddAllTitle(value, languages) {
184
- return value != null && value.length ? `Add missing ${languages.length - value.length === 1 ? "language" : "languages"}` : languages.length === 1 ? `Add ${languages[0].title} Field` : "Add all languages";
177
+ return value?.length ? `Add missing ${languages.length - value.length === 1 ? "language" : "languages"}` : languages.length === 1 ? `Add ${languages[0].title} Field` : "Add all languages";
185
178
  }
186
179
  function createValueSchemaTypeName(schemaType) {
187
180
  return `${schemaType.name}Value`;
@@ -198,11 +191,11 @@ function createAddLanguagePatches(config) {
198
191
  ...itemBase,
199
192
  _key: id
200
193
  })) : filteredLanguages.filter(
201
- (language) => value != null && value.length ? !value.find((v) => v._key === language.id) : !0
194
+ (language) => value?.length ? !value.find((v) => v._key === language.id) : !0
202
195
  ).map((language) => ({
203
196
  ...itemBase,
204
197
  _key: language.id
205
- })), languagesInUse = value != null && value.length ? value.map((v) => v) : [];
198
+ })), languagesInUse = value?.length ? value.map((v) => v) : [];
206
199
  return newItems.map((item) => {
207
200
  const languageIndex = languages.findIndex((l) => item._key === l.id), remainingLanguages = languages.slice(languageIndex + 1), nextLanguageIndex = languagesInUse.findIndex(
208
201
  (l) => (
@@ -220,7 +213,7 @@ function createAddLanguagePatches(config) {
220
213
  });
221
214
  }
222
215
  const createTranslateFieldActions = (fieldActionProps, { languages, filteredLanguages }) => languages.map((language) => {
223
- const value = useFormValue(fieldActionProps.path), disabled = value && Array.isArray(value) ? !!(value != null && value.find((item) => item._key === language.id)) : !1, hidden = !filteredLanguages.some((f) => f.id === language.id), { onChange } = useDocumentPane(), onAction = useCallback(() => {
216
+ const value = useFormValue(fieldActionProps.path), disabled = value && Array.isArray(value) ? !!value?.find((item) => item._key === language.id) : !1, hidden = !filteredLanguages.some((f) => f.id === language.id), { onChange } = useDocumentPane(), onAction = useCallback(() => {
224
217
  const { schemaType, path } = fieldActionProps, addLanguageKeys = [language.id], patches = createAddLanguagePatches({
225
218
  addLanguageKeys,
226
219
  schemaType,
@@ -262,8 +255,7 @@ const createTranslateFieldActions = (fieldActionProps, { languages, filteredLang
262
255
  }, internationalizedArrayFieldAction = defineDocumentFieldAction({
263
256
  name: "internationalizedArray",
264
257
  useAction(fieldActionProps) {
265
- var _a, _b;
266
- const isInternationalizedArrayField = (_b = (_a = fieldActionProps == null ? void 0 : fieldActionProps.schemaType) == null ? void 0 : _a.type) == null ? void 0 : _b.name.startsWith(
258
+ const isInternationalizedArrayField = fieldActionProps?.schemaType?.type?.name.startsWith(
267
259
  "internationalizedArray"
268
260
  ), { languages, filteredLanguages } = useInternationalizedArrayContext(), translateFieldActions = createTranslateFieldActions(
269
261
  fieldActionProps,
@@ -340,10 +332,9 @@ function InternationalizedArray(props) {
340
332
  [languageFilterEnabled, members, languageFilterOptions, selectedLanguageIds]
341
333
  ), handleAddLanguage = useCallback(
342
334
  async (param) => {
343
- var _a;
344
- if (!(filteredLanguages != null && filteredLanguages.length))
335
+ if (!filteredLanguages?.length)
345
336
  return;
346
- const addLanguageKeys = Array.isArray(param) ? param : [(_a = param == null ? void 0 : param.currentTarget) == null ? void 0 : _a.value].filter(Boolean), patches = createAddLanguagePatches({
337
+ const addLanguageKeys = Array.isArray(param) ? param : [param?.currentTarget?.value].filter(Boolean), patches = createAddLanguagePatches({
347
338
  addLanguageKeys,
348
339
  schemaType,
349
340
  languages,
@@ -361,23 +352,23 @@ function InternationalizedArray(props) {
361
352
  };
362
353
  }, [defaultLanguages, documentCreatedAt, handleAddLanguage, value]);
363
354
  const handleRestoreOrder = useCallback(() => {
364
- if (!(value != null && value.length) || !(languages != null && languages.length))
355
+ if (!value?.length || !languages?.length)
365
356
  return;
366
357
  const updatedValue = value.reduce((acc, v) => {
367
- const newIndex = languages.findIndex((l) => l.id === (v == null ? void 0 : v._key));
358
+ const newIndex = languages.findIndex((l) => l.id === v?._key);
368
359
  return newIndex > -1 && (acc[newIndex] = v), acc;
369
360
  }, []).filter(Boolean);
370
- (value == null ? void 0 : value.length) !== updatedValue.length && toast.push({
361
+ value?.length !== updatedValue.length && toast.push({
371
362
  title: "There was an error reordering languages",
372
363
  status: "warning"
373
364
  }), onChange(set(updatedValue));
374
- }, [toast, languages, onChange, value]), allKeysAreLanguages = useMemo(() => !(value != null && value.length) || !(languages != null && languages.length) ? !0 : value == null ? void 0 : value.every((v) => languages.find((l) => (l == null ? void 0 : l.id) === (v == null ? void 0 : v._key))), [value, languages]), languagesInUse = useMemo(
375
- () => languages && languages.length > 1 ? languages.filter((l) => value == null ? void 0 : value.find((v) => v._key === l.id)) : [],
365
+ }, [toast, languages, onChange, value]), allKeysAreLanguages = useMemo(() => !value?.length || !languages?.length ? !0 : value?.every((v) => languages.find((l) => l?.id === v?._key)), [value, languages]), languagesInUse = useMemo(
366
+ () => languages && languages.length > 1 ? languages.filter((l) => value?.find((v) => v._key === l.id)) : [],
376
367
  [languages, value]
377
- ), languagesOutOfOrder = useMemo(() => !(value != null && value.length) || !languagesInUse.length ? [] : value.map(
368
+ ), languagesOutOfOrder = useMemo(() => !value?.length || !languagesInUse.length ? [] : value.map(
378
369
  (v, vIndex) => vIndex === languagesInUse.findIndex((l) => l.id === v._key) ? null : v
379
370
  ).filter(Boolean), [value, languagesInUse]), languagesAreValid = useMemo(
380
- () => !(languages != null && languages.length) || (languages == null ? void 0 : languages.length) && languages.every((item) => item.id && item.title),
371
+ () => !languages?.length || languages?.length && languages.every((item) => item.id && item.title),
381
372
  [languages]
382
373
  );
383
374
  useEffect(() => {
@@ -392,9 +383,9 @@ function InternationalizedArray(props) {
392
383
  const addButtonsAreVisible = (
393
384
  // Plugin was configured to display buttons here (default!)
394
385
  buttonLocations.includes("field") && // There's at least one language visible
395
- (filteredLanguages == null ? void 0 : filteredLanguages.length) > 0 && // Not every language has a value yet
386
+ filteredLanguages?.length > 0 && // Not every language has a value yet
396
387
  !allLanguagesArePresent
397
- ), fieldHasMembers = (members == null ? void 0 : members.length) > 0;
388
+ ), fieldHasMembers = members?.length > 0;
398
389
  return /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
399
390
  fieldHasMembers ? /* @__PURE__ */ jsx(Fragment, { children: filteredMembers.map((member) => member.kind === "item" ? /* @__PURE__ */ createElement(
400
391
  ArrayOfObjectsItem,
@@ -407,7 +398,7 @@ function InternationalizedArray(props) {
407
398
  !addButtonsAreVisible && !fieldHasMembers ? /* @__PURE__ */ jsx(Card, { border: !0, tone: "transparent", padding: 3, radius: 2, children: /* @__PURE__ */ jsx(Text, { size: 1, children: "This internationalized field currently has no translations." }) }) : null,
408
399
  addButtonsAreVisible ? /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
409
400
  /* @__PURE__ */ jsx(
410
- AddButtons,
401
+ AddButtons$1,
411
402
  {
412
403
  languages: filteredLanguages,
413
404
  value,
@@ -430,8 +421,7 @@ function InternationalizedArray(props) {
430
421
  ] });
431
422
  }
432
423
  function getLanguagesFieldOption(schemaType) {
433
- var _a;
434
- return schemaType ? ((_a = schemaType.options) == null ? void 0 : _a.languages) || getLanguagesFieldOption(schemaType.type) : void 0;
424
+ return schemaType ? schemaType.options?.languages || getLanguagesFieldOption(schemaType.type) : void 0;
435
425
  }
436
426
  var array = (config) => {
437
427
  const { apiVersion, select, languages, type } = config, typeName = typeof type == "string" ? type : type.name, arrayName = createFieldName(typeName), objectName = createFieldName(typeName, !0);
@@ -442,8 +432,12 @@ var array = (config) => {
442
432
  components: {
443
433
  input: InternationalizedArray
444
434
  },
445
- // These options are required for validation rules – not the custom input component
446
- options: { apiVersion, select, languages },
435
+ options: {
436
+ // @ts-expect-error - these options are required for validation rules – not the custom input component
437
+ apiVersion,
438
+ select,
439
+ languages
440
+ },
447
441
  of: [
448
442
  defineField({
449
443
  ...typeof type == "string" ? {} : type,
@@ -451,15 +445,16 @@ var array = (config) => {
451
445
  type: objectName
452
446
  })
453
447
  ],
448
+ // @ts-expect-error - fix typings
454
449
  validation: (rule) => rule.custom(async (value, context) => {
455
450
  if (!value)
456
451
  return !0;
457
452
  const selectedValue = getSelectedValue(select, context.document), client = context.getClient({ apiVersion });
458
453
  let contextLanguages = [];
459
- const languagesFieldOption = getLanguagesFieldOption(context == null ? void 0 : context.type);
454
+ const languagesFieldOption = getLanguagesFieldOption(context?.type);
460
455
  if (Array.isArray(languagesFieldOption) ? contextLanguages = languagesFieldOption : Array.isArray(peek(selectedValue)) ? contextLanguages = peek(selectedValue) || [] : typeof languagesFieldOption == "function" && (contextLanguages = await languagesFieldOption(client, selectedValue)), value && value.length > contextLanguages.length)
461
456
  return `Cannot be more than ${contextLanguages.length === 1 ? "1 item" : `${contextLanguages.length} items`}`;
462
- const nonLanguageKeys = value != null && value.length ? value.filter(
457
+ const nonLanguageKeys = value?.length ? value.filter(
463
458
  (item) => !contextLanguages.find((language) => item._key === language.id)
464
459
  ) : [];
465
460
  if (nonLanguageKeys.length)
@@ -467,10 +462,10 @@ var array = (config) => {
467
462
  message: "Array item keys must be valid languages registered to the field type",
468
463
  paths: nonLanguageKeys.map((item) => [{ _key: item._key }])
469
464
  };
470
- const valuesByLanguage = value != null && value.length ? value.filter((item) => !!(item != null && item._key)).reduce((acc, cur) => acc[cur._key] ? { ...acc, [cur._key]: [...acc[cur._key], cur] } : {
465
+ const valuesByLanguage = value?.length ? value.filter((item) => !!item?._key).reduce((acc, cur) => acc[cur._key] ? { ...acc, [cur._key]: [...acc[cur._key], cur] } : {
471
466
  ...acc,
472
467
  [cur._key]: [cur]
473
- }, {}) : {}, duplicateValues = Object.values(valuesByLanguage).filter((item) => (item == null ? void 0 : item.length) > 1).flat();
468
+ }, {}) : {}, duplicateValues = Object.values(valuesByLanguage).filter((item) => item?.length > 1).flat();
474
469
  return duplicateValues.length ? {
475
470
  message: "There can only be one field per language",
476
471
  paths: duplicateValues.map((item) => [{ _key: item._key }])
@@ -486,7 +481,7 @@ function InternationalizedField(props) {
486
481
  }) : props.children;
487
482
  }
488
483
  function getToneFromValidation(validations) {
489
- if (!(validations != null && validations.length))
484
+ if (!validations?.length)
490
485
  return;
491
486
  const validationLevels = validations.map((v) => v.level);
492
487
  if (validationLevels.includes("error"))
@@ -507,16 +502,12 @@ function InternationalizedInput(props) {
507
502
  // TODO: Remove this as it shouldn't be necessary?
508
503
  value: props.value
509
504
  }, { validation, value, onChange, readOnly } = inlineProps, { languages } = useInternationalizedArrayContext(), languageKeysInUse = useMemo(
510
- () => {
511
- var _a;
512
- return (_a = parentValue == null ? void 0 : parentValue.map((v) => v._key)) != null ? _a : [];
513
- },
505
+ () => parentValue?.map((v) => v._key) ?? [],
514
506
  [parentValue]
515
- ), keyIsValid = languages != null && languages.length ? languages.find((l) => l.id === value._key) : !1, handleKeyChange = useCallback(
507
+ ), keyIsValid = languages?.length ? languages.find((l) => l.id === value._key) : !1, handleKeyChange = useCallback(
516
508
  (event) => {
517
- var _a;
518
- const languageId = (_a = event == null ? void 0 : event.currentTarget) == null ? void 0 : _a.value;
519
- !value || !(languages != null && languages.length) || !languages.find((l) => l.id === languageId) || onChange([set(languageId, ["_key"])]);
509
+ const languageId = event?.currentTarget?.value;
510
+ !value || !languages?.length || !languages.find((l) => l.id === languageId) || onChange([set(languageId, ["_key"])]);
520
511
  },
521
512
  [onChange, value, languages]
522
513
  ), handleUnset = useCallback(() => {
@@ -564,9 +555,9 @@ var object = (config) => {
564
555
  title: `Internationalized array ${type}`,
565
556
  type: "object",
566
557
  components: {
558
+ // @ts-expect-error - fix typings
567
559
  item: InternationalizedInput
568
560
  },
569
- // @ts-expect-error - Address this typing issue with the inner object
570
561
  fields: [
571
562
  typeof type == "string" ? (
572
563
  // Define a simple field if all we have is the name as a string
@@ -652,10 +643,13 @@ const internationalizedArray = definePlugin((config) => {
652
643
  (field) => field.type.name
653
644
  ).some(
654
645
  (name) => name.startsWith("internationalizedArray")
655
- ) ? props.renderDefault(props) : InternationalizedArrayProvider({
656
- ...props,
657
- internationalizedArray: pluginConfig
658
- })
646
+ ) ? props.renderDefault(props) : /* @__PURE__ */ jsx(
647
+ InternationalizedArrayProvider,
648
+ {
649
+ ...props,
650
+ internationalizedArray: pluginConfig
651
+ }
652
+ )
659
653
  }
660
654
  },
661
655
  // Register custom schema types for the outer array and the inner object