@sanity/document-internationalization 2.0.3 → 2.1.1

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 CHANGED
@@ -169,6 +169,23 @@ defineField({
169
169
  })
170
170
  ```
171
171
 
172
+ ### Excluding fields
173
+
174
+ The default behaviour of this plugin when creating a new translation is to duplicate the originating document, which is useful for then translating the fields directly in the new document - perhaps with [Sanity AI Assist](https://github.com/sanity-io/assist). However, sometimes you may want to exclude certain fields from being copied to the new document. You can do this by updating your schema to exclude certain types or fields with `options.documentInternationalization.exclude`:
175
+
176
+ ```ts
177
+ defineField({
178
+ name: 'title',
179
+ title: 'Title',
180
+ type: 'string',
181
+ options: {
182
+ documentInternationalization: {
183
+ exclude: true,
184
+ },
185
+ },
186
+ }),
187
+ ```
188
+
172
189
  ## Querying translations
173
190
 
174
191
  ### Querying with GROQ
package/dist/index.d.ts CHANGED
@@ -20,6 +20,13 @@ export declare type DocumentInternationalizationMenuProps = {
20
20
  documentId: string
21
21
  }
22
22
 
23
+ export declare interface DocumentInternationalizationSchemaOpts {
24
+ documentInternationalization?: {
25
+ /** Set to true to disable duplication of this field or type */
26
+ exclude?: boolean
27
+ }
28
+ }
29
+
23
30
  export declare type Language = {
24
31
  id: Intl.UnicodeBCP47LocaleIdentifier
25
32
  title: string
@@ -57,3 +64,25 @@ export declare type TranslationReference = KeyedObject & {
57
64
  export declare function useDocumentInternationalizationContext(): PluginConfigContext
58
65
 
59
66
  export {}
67
+
68
+ declare module 'sanity' {
69
+ interface ArrayOptions extends DocumentInternationalizationSchemaOpts {}
70
+ interface BlockOptions extends DocumentInternationalizationSchemaOpts {}
71
+ interface BooleanOptions extends DocumentInternationalizationSchemaOpts {}
72
+ interface CrossDatasetReferenceOptions
73
+ extends DocumentInternationalizationSchemaOpts {}
74
+ interface DateOptions extends DocumentInternationalizationSchemaOpts {}
75
+ interface DatetimeOptions extends DocumentInternationalizationSchemaOpts {}
76
+ interface FileOptions extends DocumentInternationalizationSchemaOpts {}
77
+ interface GeopointOptions extends DocumentInternationalizationSchemaOpts {}
78
+ interface ImageOptions extends DocumentInternationalizationSchemaOpts {}
79
+ interface NumberOptions extends DocumentInternationalizationSchemaOpts {}
80
+ interface ObjectOptions extends DocumentInternationalizationSchemaOpts {}
81
+ interface ReferenceBaseOptions
82
+ extends DocumentInternationalizationSchemaOpts {}
83
+ interface SlugOptions extends DocumentInternationalizationSchemaOpts {}
84
+ interface StringOptions extends DocumentInternationalizationSchemaOpts {}
85
+ interface TextOptions extends DocumentInternationalizationSchemaOpts {}
86
+ interface UrlOptions extends DocumentInternationalizationSchemaOpts {}
87
+ interface EmailOptions extends DocumentInternationalizationSchemaOpts {}
88
+ }
package/dist/index.esm.js CHANGED
@@ -2,11 +2,12 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { TrashIcon, CogIcon, SplitVerticalIcon, CheckmarkIcon, AddIcon, EditIcon, TranslateIcon, InfoOutlineIcon } from '@sanity/icons';
3
3
  import { Flex, Spinner, Stack, Text, Card, Grid, Button, useToast, Tooltip, Box, Badge, useClickOutside, TextInput, Popover, Inline, Dialog } from '@sanity/ui';
4
4
  import { useMemo, useEffect, createContext, useContext, useState, useCallback } from 'react';
5
- import { useSchema, Preview, useClient, useEditState, useValidationStatus, TextWithTone, useWorkspace, PatchEvent, unset, defineType, defineField, definePlugin, isSanityDocument } from 'sanity';
5
+ import { useSchema, Preview, useClient, useWorkspace, isDocumentSchemaType, pathToString, useEditState, useValidationStatus, TextWithTone, PatchEvent, unset, defineType, defineField, definePlugin, isSanityDocument } from 'sanity';
6
6
  import { Feedback, useListeningQuery } from 'sanity-plugin-utils';
7
7
  import { uuid } from '@sanity/uuid';
8
8
  import { usePaneRouter, useDocumentPane } from 'sanity/desk';
9
9
  import { RouterContext } from 'sanity/router';
10
+ import { Mutation, extractWithPath } from '@sanity/mutator';
10
11
  import styled from 'styled-components';
11
12
  import { internationalizedArray } from 'sanity-plugin-internationalized-array';
12
13
  function DocumentPreview(props) {
@@ -218,6 +219,7 @@ function DocumentInternationalizationProvider(props) {
218
219
  const client = useClient({
219
220
  apiVersion: pluginConfig.apiVersion
220
221
  });
222
+ const workspace = useWorkspace();
221
223
  const supportedLanguages = Array.isArray(pluginConfig.supportedLanguages) ? pluginConfig.supportedLanguages :
222
224
  // eslint-disable-next-line require-await
223
225
  suspend(async () => {
@@ -225,7 +227,7 @@ function DocumentInternationalizationProvider(props) {
225
227
  return pluginConfig.supportedLanguages(client);
226
228
  }
227
229
  return pluginConfig.supportedLanguages;
228
- }, []);
230
+ }, [workspace]);
229
231
  return /* @__PURE__ */jsx(DocumentInternationalizationContext.Provider, {
230
232
  value: {
231
233
  ...pluginConfig,
@@ -403,6 +405,77 @@ function createReference(key, ref, type) {
403
405
  }
404
406
  };
405
407
  }
408
+ function removeExcludedPaths(doc, schemaType) {
409
+ if (!isDocumentSchemaType(schemaType) || !doc) {
410
+ return doc;
411
+ }
412
+ const pathsToExclude = extractPaths(doc, schemaType, []).filter(field => {
413
+ var _a, _b, _c;
414
+ return ((_c = (_b = (_a = field.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.documentInternationalization) == null ? void 0 : _c.exclude) === true;
415
+ }).map(field => {
416
+ return pathToString(field.path);
417
+ });
418
+ const mut = new Mutation({
419
+ mutations: [{
420
+ patch: {
421
+ id: doc._id,
422
+ unset: pathsToExclude
423
+ }
424
+ }]
425
+ });
426
+ return mut.apply(doc);
427
+ }
428
+ function extractPaths(doc, schemaType, path) {
429
+ return schemaType.fields.reduce((acc, field) => {
430
+ var _a, _b;
431
+ const fieldPath = [...path, field.name];
432
+ const fieldSchema = field.type;
433
+ const {
434
+ value
435
+ } = (_a = extractWithPath(pathToString(fieldPath), doc)[0]) != null ? _a : {};
436
+ if (!value) {
437
+ return acc;
438
+ }
439
+ const thisFieldWithPath = {
440
+ path: fieldPath,
441
+ name: field.name,
442
+ schemaType: fieldSchema,
443
+ value
444
+ };
445
+ if (fieldSchema.jsonType === "object") {
446
+ const innerFields = extractPaths(doc, fieldSchema, fieldPath);
447
+ return [...acc, thisFieldWithPath, ...innerFields];
448
+ } else if (fieldSchema.jsonType === "array" && fieldSchema.of.length && fieldSchema.of.some(item => "fields" in item)) {
449
+ const {
450
+ value: arrayValue
451
+ } = (_b = extractWithPath(pathToString(fieldPath), doc)[0]) != null ? _b : {};
452
+ let arrayPaths = [];
453
+ if (arrayValue == null ? void 0 : arrayValue.length) {
454
+ for (const item of arrayValue) {
455
+ const itemPath = [...fieldPath, {
456
+ _key: item._key
457
+ }];
458
+ let itemSchema = fieldSchema.of.find(t => t.name === item._type);
459
+ if (!item._type) {
460
+ itemSchema = fieldSchema.of[0];
461
+ }
462
+ if (item._key && itemSchema) {
463
+ const innerFields = extractPaths(doc, itemSchema, itemPath);
464
+ const arrayMember = {
465
+ path: itemPath,
466
+ name: item._key,
467
+ schemaType: itemSchema,
468
+ value: item
469
+ };
470
+ arrayPaths = [...arrayPaths, arrayMember, ...innerFields];
471
+ }
472
+ }
473
+ }
474
+ return [...acc, thisFieldWithPath, ...arrayPaths];
475
+ }
476
+ return [...acc, thisFieldWithPath];
477
+ }, []);
478
+ }
406
479
  function LanguageOption(props) {
407
480
  var _a;
408
481
  const {
@@ -426,7 +499,7 @@ function LanguageOption(props) {
426
499
  apiVersion
427
500
  });
428
501
  const toast = useToast();
429
- const open = useOpenInNewPane((_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref, schemaType);
502
+ const open = useOpenInNewPane((_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref, schemaType.name);
430
503
  const handleOpen = useCallback(() => open(), [open]);
431
504
  const handleCreate = useCallback(async () => {
432
505
  if (!source) {
@@ -440,19 +513,20 @@ function LanguageOption(props) {
440
513
  }
441
514
  const transaction = client.transaction();
442
515
  const newTranslationDocumentId = uuid();
443
- const newTranslationDocument = {
516
+ let newTranslationDocument = {
444
517
  ...source,
445
518
  _id: "drafts.".concat(newTranslationDocumentId),
446
519
  // 2. Update language of the translation
447
520
  [languageField]: language.id
448
521
  };
522
+ newTranslationDocument = removeExcludedPaths(newTranslationDocument, schemaType);
449
523
  transaction.create(newTranslationDocument);
450
- const sourceReference = createReference(sourceLanguageId, documentId, schemaType, !weakReferences);
451
- const newTranslationReference = createReference(language.id, newTranslationDocumentId, schemaType, !weakReferences);
524
+ const sourceReference = createReference(sourceLanguageId, documentId, schemaType.name, !weakReferences);
525
+ const newTranslationReference = createReference(language.id, newTranslationDocumentId, schemaType.name, !weakReferences);
452
526
  const newMetadataDocument = {
453
527
  _id: metadataId,
454
528
  _type: METADATA_SCHEMA_NAME,
455
- schemaTypes: [schemaType],
529
+ schemaTypes: [schemaType.name],
456
530
  translations: [sourceReference]
457
531
  };
458
532
  transaction.createIfNotExists(newMetadataDocument);
@@ -605,7 +679,7 @@ function DocumentInternationalizationMenu(props) {
605
679
  const {
606
680
  documentId
607
681
  } = props;
608
- const schemaType = props.schemaType.name;
682
+ const schemaType = props.schemaType;
609
683
  const {
610
684
  languageField,
611
685
  supportedLanguages
@@ -640,7 +714,7 @@ function DocumentInternationalizationMenu(props) {
640
714
  const {
641
715
  draft,
642
716
  published
643
- } = useEditState(documentId, schemaType);
717
+ } = useEditState(documentId, schemaType.name);
644
718
  const source = draft || published;
645
719
  const documentIsInOneMetadataDocument = useMemo(() => {
646
720
  return Array.isArray(data) && data.length <= 1;
@@ -725,7 +799,7 @@ function DocumentInternationalizationMenu(props) {
725
799
  if (!documentId) {
726
800
  return null;
727
801
  }
728
- if (!schemaType) {
802
+ if (!schemaType || !schemaType.name) {
729
803
  return null;
730
804
  }
731
805
  return /* @__PURE__ */jsx(Popover, {