@sanity/document-internationalization 2.0.2 → 2.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Sanity.io
3
+ Copyright (c) 2024 Sanity.io
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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, isDocumentSchemaType, pathToString, useEditState, useValidationStatus, TextWithTone, useWorkspace, 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) {
@@ -403,6 +404,77 @@ function createReference(key, ref, type) {
403
404
  }
404
405
  };
405
406
  }
407
+ function removeExcludedPaths(doc, schemaType) {
408
+ if (!isDocumentSchemaType(schemaType) || !doc) {
409
+ return doc;
410
+ }
411
+ const pathsToExclude = extractPaths(doc, schemaType, []).filter(field => {
412
+ var _a, _b, _c;
413
+ return ((_c = (_b = (_a = field.schemaType) == null ? void 0 : _a.options) == null ? void 0 : _b.documentInternationalization) == null ? void 0 : _c.exclude) === true;
414
+ }).map(field => {
415
+ return pathToString(field.path);
416
+ });
417
+ const mut = new Mutation({
418
+ mutations: [{
419
+ patch: {
420
+ id: doc._id,
421
+ unset: pathsToExclude
422
+ }
423
+ }]
424
+ });
425
+ return mut.apply(doc);
426
+ }
427
+ function extractPaths(doc, schemaType, path) {
428
+ return schemaType.fields.reduce((acc, field) => {
429
+ var _a, _b;
430
+ const fieldPath = [...path, field.name];
431
+ const fieldSchema = field.type;
432
+ const {
433
+ value
434
+ } = (_a = extractWithPath(pathToString(fieldPath), doc)[0]) != null ? _a : {};
435
+ if (!value) {
436
+ return acc;
437
+ }
438
+ const thisFieldWithPath = {
439
+ path: fieldPath,
440
+ name: field.name,
441
+ schemaType: fieldSchema,
442
+ value
443
+ };
444
+ if (fieldSchema.jsonType === "object") {
445
+ const innerFields = extractPaths(doc, fieldSchema, fieldPath);
446
+ return [...acc, thisFieldWithPath, ...innerFields];
447
+ } else if (fieldSchema.jsonType === "array" && fieldSchema.of.length && fieldSchema.of.some(item => "fields" in item)) {
448
+ const {
449
+ value: arrayValue
450
+ } = (_b = extractWithPath(pathToString(fieldPath), doc)[0]) != null ? _b : {};
451
+ let arrayPaths = [];
452
+ if (arrayValue == null ? void 0 : arrayValue.length) {
453
+ for (const item of arrayValue) {
454
+ const itemPath = [...fieldPath, {
455
+ _key: item._key
456
+ }];
457
+ let itemSchema = fieldSchema.of.find(t => t.name === item._type);
458
+ if (!item._type) {
459
+ itemSchema = fieldSchema.of[0];
460
+ }
461
+ if (item._key && itemSchema) {
462
+ const innerFields = extractPaths(doc, itemSchema, itemPath);
463
+ const arrayMember = {
464
+ path: itemPath,
465
+ name: item._key,
466
+ schemaType: itemSchema,
467
+ value: item
468
+ };
469
+ arrayPaths = [...arrayPaths, arrayMember, ...innerFields];
470
+ }
471
+ }
472
+ }
473
+ return [...acc, thisFieldWithPath, ...arrayPaths];
474
+ }
475
+ return [...acc, thisFieldWithPath];
476
+ }, []);
477
+ }
406
478
  function LanguageOption(props) {
407
479
  var _a;
408
480
  const {
@@ -426,7 +498,7 @@ function LanguageOption(props) {
426
498
  apiVersion
427
499
  });
428
500
  const toast = useToast();
429
- const open = useOpenInNewPane((_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref, schemaType);
501
+ const open = useOpenInNewPane((_a = translation == null ? void 0 : translation.value) == null ? void 0 : _a._ref, schemaType.name);
430
502
  const handleOpen = useCallback(() => open(), [open]);
431
503
  const handleCreate = useCallback(async () => {
432
504
  if (!source) {
@@ -440,19 +512,20 @@ function LanguageOption(props) {
440
512
  }
441
513
  const transaction = client.transaction();
442
514
  const newTranslationDocumentId = uuid();
443
- const newTranslationDocument = {
515
+ let newTranslationDocument = {
444
516
  ...source,
445
517
  _id: "drafts.".concat(newTranslationDocumentId),
446
518
  // 2. Update language of the translation
447
519
  [languageField]: language.id
448
520
  };
521
+ newTranslationDocument = removeExcludedPaths(newTranslationDocument, schemaType);
449
522
  transaction.create(newTranslationDocument);
450
- const sourceReference = createReference(sourceLanguageId, documentId, schemaType, !weakReferences);
451
- const newTranslationReference = createReference(language.id, newTranslationDocumentId, schemaType, !weakReferences);
523
+ const sourceReference = createReference(sourceLanguageId, documentId, schemaType.name, !weakReferences);
524
+ const newTranslationReference = createReference(language.id, newTranslationDocumentId, schemaType.name, !weakReferences);
452
525
  const newMetadataDocument = {
453
526
  _id: metadataId,
454
527
  _type: METADATA_SCHEMA_NAME,
455
- schemaTypes: [schemaType],
528
+ schemaTypes: [schemaType.name],
456
529
  translations: [sourceReference]
457
530
  };
458
531
  transaction.createIfNotExists(newMetadataDocument);
@@ -605,7 +678,7 @@ function DocumentInternationalizationMenu(props) {
605
678
  const {
606
679
  documentId
607
680
  } = props;
608
- const schemaType = props.schemaType.name;
681
+ const schemaType = props.schemaType;
609
682
  const {
610
683
  languageField,
611
684
  supportedLanguages
@@ -640,7 +713,7 @@ function DocumentInternationalizationMenu(props) {
640
713
  const {
641
714
  draft,
642
715
  published
643
- } = useEditState(documentId, schemaType);
716
+ } = useEditState(documentId, schemaType.name);
644
717
  const source = draft || published;
645
718
  const documentIsInOneMetadataDocument = useMemo(() => {
646
719
  return Array.isArray(data) && data.length <= 1;
@@ -725,7 +798,7 @@ function DocumentInternationalizationMenu(props) {
725
798
  if (!documentId) {
726
799
  return null;
727
800
  }
728
- if (!schemaType) {
801
+ if (!schemaType || !schemaType.name) {
729
802
  return null;
730
803
  }
731
804
  return /* @__PURE__ */jsx(Popover, {