gt-sanity 0.0.6 → 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.md +1 -1
- package/dist/index.d.mts +95 -73
- package/dist/index.d.ts +95 -73
- package/dist/index.js +9066 -1207
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9083 -1197
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -3
- package/src/adapter/core.ts +41 -4
- package/src/adapter/getLocales.ts +2 -2
- package/src/components/TranslationsProvider.tsx +942 -0
- package/src/components/page/BatchProgress.tsx +27 -0
- package/src/components/page/ImportAllDialog.tsx +51 -0
- package/src/components/page/ImportMissingDialog.tsx +55 -0
- package/src/components/page/TranslateAllDialog.tsx +55 -0
- package/src/components/page/TranslationsTable.tsx +81 -0
- package/src/components/page/TranslationsTool.tsx +299 -837
- package/src/components/shared/BaseTranslationWrapper.tsx +82 -0
- package/src/components/shared/LocaleCheckbox.tsx +47 -0
- package/src/components/shared/SingleDocumentView.tsx +108 -0
- package/src/components/tab/TranslationView.tsx +379 -0
- package/src/components/tab/TranslationsTab.tsx +25 -0
- package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +6 -9
- package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +5 -24
- package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +3 -23
- package/src/configuration/baseDocumentLevelConfig/index.ts +16 -68
- package/src/configuration/baseFieldLevelConfig.ts +15 -50
- package/src/index.ts +29 -43
- package/src/sanity-api/findDocuments.ts +44 -0
- package/src/sanity-api/publishDocuments.ts +49 -0
- package/src/sanity-api/resolveRefs.ts +146 -0
- package/src/serialization/BaseDocumentMerger.ts +138 -0
- package/src/serialization/BaseSerializationConfig.ts +220 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/documentLevelDeserialization.test.ts.snap +189 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/fieldLevelDeserialization.test.ts.snap +107 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/baseDeserialization.test.ts +397 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/documentLevelDeserialization.test.ts +107 -0
- package/src/serialization/__tests__/BaseDocumentDeserializer/fieldLevelDeserialization.test.ts +107 -0
- package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/documentLevelMerge.test.ts.snap +193 -0
- package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/fieldLevelMerge.test.ts.snap +97 -0
- package/src/serialization/__tests__/BaseDocumentMerger/baseMerge.test.ts +36 -0
- package/src/serialization/__tests__/BaseDocumentMerger/documentLevelMerge.test.ts +96 -0
- package/src/serialization/__tests__/BaseDocumentMerger/fieldLevelMerge.test.ts +142 -0
- package/src/serialization/__tests__/BaseDocumentMerger/utils.ts +52 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentInlineMarks.test.ts.snap +39 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentLevelSerialization.test.ts.snap +8 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/fieldLevelSerialization.test.ts.snap +8 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/baseSerialization.test.ts +345 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/documentInlineMarks.test.ts +53 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/documentLevelSerialization.test.ts +120 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/fieldLevelSerialization.test.ts +153 -0
- package/src/serialization/__tests__/BaseDocumentSerializer/utils.ts +27 -0
- package/src/serialization/__tests__/README +2 -0
- package/src/serialization/__tests__/__fixtures__/annotationAndInlineBlocks.json +140 -0
- package/src/serialization/__tests__/__fixtures__/customStyles.json +62 -0
- package/src/serialization/__tests__/__fixtures__/documentInlineMarks.json +70 -0
- package/src/serialization/__tests__/__fixtures__/documentLevelArticle.json +185 -0
- package/src/serialization/__tests__/__fixtures__/fieldLevelArticle.json +107 -0
- package/src/serialization/__tests__/__fixtures__/inlineDocumentLevelArticle.json +134 -0
- package/src/serialization/__tests__/__fixtures__/inlineSchema.ts +270 -0
- package/src/serialization/__tests__/__fixtures__/messy-html.html +26 -0
- package/src/serialization/__tests__/__fixtures__/nestedLanguageFields.json +54 -0
- package/src/serialization/__tests__/__fixtures__/schema.ts +310 -0
- package/src/serialization/__tests__/global.setup.ts +40 -0
- package/src/serialization/__tests__/helpers.ts +132 -0
- package/src/serialization/data.ts +82 -0
- package/src/serialization/deserialize/BaseDocumentDeserializer.ts +171 -0
- package/src/serialization/deserialize/helpers.ts +42 -0
- package/src/serialization/helpers.ts +18 -0
- package/src/serialization/index.ts +11 -0
- package/src/serialization/serialize/fieldFilters.ts +124 -0
- package/src/serialization/serialize/index.ts +284 -0
- package/src/serialization/types.ts +41 -0
- package/src/translation/importDocument.ts +4 -5
- package/src/translation/uploadFiles.ts +1 -1
- package/src/types.ts +3 -19
- package/src/utils/batchProcessor.ts +111 -0
- package/src/utils/importUtils.ts +95 -0
- package/src/utils/serialize.ts +25 -5
- package/src/utils/shared.ts +1 -1
- package/src/adapter/index.ts +0 -13
- package/src/components/NewTask.tsx +0 -251
- package/src/components/TaskView.tsx +0 -257
- package/src/components/TranslationContext.tsx +0 -24
- package/src/components/TranslationView.tsx +0 -114
- package/src/components/TranslationsTab.tsx +0 -181
- /package/src/components/{LanguageStatus.tsx → shared/LanguageStatus.tsx} +0 -0
- /package/src/components/{ProgressBar.tsx → shared/ProgressBar.tsx} +0 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { Schema } from '@sanity/schema';
|
|
2
|
+
|
|
3
|
+
const arrayField = {
|
|
4
|
+
name: 'arrayField',
|
|
5
|
+
title: 'Array Field',
|
|
6
|
+
type: 'array',
|
|
7
|
+
of: [
|
|
8
|
+
{
|
|
9
|
+
type: 'block',
|
|
10
|
+
marks: {
|
|
11
|
+
annotations: [{ type: 'linkField' }],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
{ type: 'objectField' },
|
|
15
|
+
{ type: 'linkField' },
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
const linkField = {
|
|
19
|
+
name: 'linkField',
|
|
20
|
+
title: 'Link Field',
|
|
21
|
+
type: 'object',
|
|
22
|
+
fields: [
|
|
23
|
+
{
|
|
24
|
+
name: 'label',
|
|
25
|
+
title: 'Label',
|
|
26
|
+
type: 'string',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'linkType',
|
|
30
|
+
title: 'Link Type',
|
|
31
|
+
type: 'string',
|
|
32
|
+
options: {
|
|
33
|
+
list: [
|
|
34
|
+
{ title: 'None', value: 'none' },
|
|
35
|
+
{ title: 'URL', value: 'href' },
|
|
36
|
+
{ title: 'Page', value: 'page' },
|
|
37
|
+
{ title: 'Simple Page', value: 'simplePage' },
|
|
38
|
+
{ title: 'Post', value: 'post' },
|
|
39
|
+
{ title: 'File', value: 'file' },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'href',
|
|
45
|
+
title: 'URL',
|
|
46
|
+
type: 'url',
|
|
47
|
+
hidden: ({ parent }: { parent: any }) => parent?.linkType !== 'href',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'page',
|
|
51
|
+
title: 'Page',
|
|
52
|
+
type: 'reference',
|
|
53
|
+
to: [{ type: 'page' }],
|
|
54
|
+
weak: true,
|
|
55
|
+
hidden: ({ parent }: { parent: any }) => parent?.linkType !== 'page',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'simplePage',
|
|
59
|
+
title: 'Simple Page',
|
|
60
|
+
type: 'reference',
|
|
61
|
+
to: [{ type: 'simplePage' }],
|
|
62
|
+
weak: true,
|
|
63
|
+
hidden: ({ parent }: { parent: any }) =>
|
|
64
|
+
parent?.linkType !== 'simplePage',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'post',
|
|
68
|
+
title: 'Post',
|
|
69
|
+
type: 'reference',
|
|
70
|
+
to: [{ type: 'post' }],
|
|
71
|
+
weak: true,
|
|
72
|
+
hidden: ({ parent }: { parent: any }) => parent?.linkType !== 'post',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'file',
|
|
76
|
+
title: 'File',
|
|
77
|
+
type: 'file',
|
|
78
|
+
hidden: ({ parent }: { parent: any }) => parent?.linkType !== 'file',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'openInNewTab',
|
|
82
|
+
title: 'Open in New Tab',
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const childObjectField = {
|
|
89
|
+
name: 'childObjectField',
|
|
90
|
+
title: 'Child Object Field',
|
|
91
|
+
type: 'object',
|
|
92
|
+
fields: [
|
|
93
|
+
{
|
|
94
|
+
name: 'title',
|
|
95
|
+
title: 'Title',
|
|
96
|
+
type: 'string',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'content',
|
|
100
|
+
title: 'Content',
|
|
101
|
+
type: 'array',
|
|
102
|
+
of: [
|
|
103
|
+
{
|
|
104
|
+
type: 'block',
|
|
105
|
+
marks: {
|
|
106
|
+
annotations: [{ type: 'linkField' }],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{ type: 'linkField' },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const objectField = {
|
|
116
|
+
name: 'objectField',
|
|
117
|
+
title: 'Object Field',
|
|
118
|
+
type: 'object',
|
|
119
|
+
fields: [
|
|
120
|
+
{
|
|
121
|
+
name: 'title',
|
|
122
|
+
title: 'Title',
|
|
123
|
+
type: 'string',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'objectAsField',
|
|
127
|
+
title: 'Object As Field',
|
|
128
|
+
type: 'childObjectField',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'nestedArrayField',
|
|
132
|
+
title: 'Nested Array Field',
|
|
133
|
+
type: 'array',
|
|
134
|
+
of: [
|
|
135
|
+
{
|
|
136
|
+
type: 'block',
|
|
137
|
+
marks: {
|
|
138
|
+
annotations: [{ type: 'linkField' }],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{ type: 'childObjectField' },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const documentLevelArticle = {
|
|
148
|
+
name: 'documentLevelArticle',
|
|
149
|
+
title: 'Document Level Article',
|
|
150
|
+
type: 'document',
|
|
151
|
+
fields: [
|
|
152
|
+
{
|
|
153
|
+
name: 'title',
|
|
154
|
+
title: 'Title',
|
|
155
|
+
type: 'string',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'meta',
|
|
159
|
+
title: 'Meta',
|
|
160
|
+
type: 'string',
|
|
161
|
+
localize: false,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'snippet',
|
|
165
|
+
title: 'Snippet',
|
|
166
|
+
type: 'text',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: 'tags',
|
|
170
|
+
title: 'Tags',
|
|
171
|
+
type: 'array',
|
|
172
|
+
of: [{ type: 'string' }],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'hidden',
|
|
176
|
+
title: 'Hidden',
|
|
177
|
+
type: 'boolean',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'config',
|
|
181
|
+
title: 'Config',
|
|
182
|
+
type: 'objectField',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: 'content',
|
|
186
|
+
title: 'Content',
|
|
187
|
+
type: 'arrayField',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
function createLocaleFields(locales: string[], fieldType: Record<string, any>) {
|
|
193
|
+
return locales.map((locale) => ({
|
|
194
|
+
...{ name: locale },
|
|
195
|
+
...fieldType,
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const fieldLevelArticle = {
|
|
200
|
+
name: 'fieldLevelArticle',
|
|
201
|
+
title: 'Field Level Article',
|
|
202
|
+
type: 'document',
|
|
203
|
+
fields: [
|
|
204
|
+
{
|
|
205
|
+
name: 'title',
|
|
206
|
+
title: 'Title',
|
|
207
|
+
type: 'localeString',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'meta',
|
|
211
|
+
title: 'Meta',
|
|
212
|
+
type: 'string',
|
|
213
|
+
localize: false,
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'snippet',
|
|
217
|
+
title: 'Snippet',
|
|
218
|
+
type: 'object',
|
|
219
|
+
fields: createLocaleFields(['en', 'fr', 'de'], { type: 'text' }),
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'tags',
|
|
223
|
+
title: 'Tags',
|
|
224
|
+
type: 'object',
|
|
225
|
+
fields: createLocaleFields(['en', 'fr', 'de'], {
|
|
226
|
+
type: 'array',
|
|
227
|
+
of: [{ type: 'string' }],
|
|
228
|
+
}),
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'hidden',
|
|
232
|
+
title: 'Hidden',
|
|
233
|
+
type: 'boolean',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: 'config',
|
|
237
|
+
title: 'Config',
|
|
238
|
+
type: 'object',
|
|
239
|
+
fields: createLocaleFields(['en', 'fr', 'de'], { type: 'objectField' }),
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'content',
|
|
243
|
+
title: 'Content',
|
|
244
|
+
type: 'object',
|
|
245
|
+
fields: createLocaleFields(['en', 'fr', 'de'], { type: 'arrayField' }),
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'slices',
|
|
249
|
+
title: 'Slices',
|
|
250
|
+
type: 'array',
|
|
251
|
+
of: [
|
|
252
|
+
{ type: 'localeBlock' },
|
|
253
|
+
{ type: 'reference', to: [{ type: 'marketText' }] },
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: 'pageFields',
|
|
258
|
+
title: 'Page Fields',
|
|
259
|
+
type: 'pageFields',
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const localeBlock = {
|
|
265
|
+
name: 'localeBlock',
|
|
266
|
+
title: 'Locale Block',
|
|
267
|
+
type: 'object',
|
|
268
|
+
fields: createLocaleFields(['en', 'fr_FR', 'de_DE'], { type: 'arrayField' }),
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const localeString = {
|
|
272
|
+
name: 'localeString',
|
|
273
|
+
title: 'Locale String',
|
|
274
|
+
type: 'object',
|
|
275
|
+
fields: createLocaleFields(['en', 'fr_FR', 'de_DE'], { type: 'string' }),
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const pageFields = {
|
|
279
|
+
name: 'pageFields',
|
|
280
|
+
title: 'Page Fields',
|
|
281
|
+
type: 'object',
|
|
282
|
+
fields: [
|
|
283
|
+
{
|
|
284
|
+
title: 'Page Name',
|
|
285
|
+
name: 'name',
|
|
286
|
+
type: 'localeString',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: 'slug',
|
|
290
|
+
type: 'string',
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const types = [
|
|
296
|
+
arrayField,
|
|
297
|
+
linkField,
|
|
298
|
+
childObjectField,
|
|
299
|
+
objectField,
|
|
300
|
+
documentLevelArticle,
|
|
301
|
+
fieldLevelArticle,
|
|
302
|
+
pageFields,
|
|
303
|
+
localeBlock,
|
|
304
|
+
localeString,
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
export default new Schema({
|
|
308
|
+
name: 'test',
|
|
309
|
+
types,
|
|
310
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PortableTextTextBlock,
|
|
3
|
+
PortableTextSpan,
|
|
4
|
+
PortableTextObject,
|
|
5
|
+
} from 'sanity';
|
|
6
|
+
import { vi } from 'vitest';
|
|
7
|
+
|
|
8
|
+
let mockTestKey = 0;
|
|
9
|
+
|
|
10
|
+
vi.mock('@portabletext/block-tools', async () => {
|
|
11
|
+
const originalModule = await vi.importActual<
|
|
12
|
+
typeof import('@portabletext/block-tools')
|
|
13
|
+
>('@portabletext/block-tools');
|
|
14
|
+
return {
|
|
15
|
+
...originalModule,
|
|
16
|
+
//not ideal but vi.mock('@sanity/block-tools/src/util/randomKey.ts' is not working
|
|
17
|
+
htmlToBlocks: (html: string, blockContentType: any, options: any) => {
|
|
18
|
+
const blocks = originalModule.htmlToBlocks(
|
|
19
|
+
html,
|
|
20
|
+
blockContentType,
|
|
21
|
+
options
|
|
22
|
+
);
|
|
23
|
+
const newBlocks = blocks.map((block) => {
|
|
24
|
+
const newChildren = (
|
|
25
|
+
block as unknown as PortableTextTextBlock<
|
|
26
|
+
PortableTextSpan | PortableTextObject
|
|
27
|
+
>
|
|
28
|
+
).children.map((child) => {
|
|
29
|
+
return { ...child, _key: `randomKey-${mockTestKey++}` };
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
...block,
|
|
33
|
+
children: newChildren,
|
|
34
|
+
_key: `randomKey-${mockTestKey++}`,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
return newBlocks;
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { BaseDocumentSerializer, BaseDocumentDeserializer } from '../index';
|
|
2
|
+
import {
|
|
3
|
+
customSerializers,
|
|
4
|
+
customDeserializers,
|
|
5
|
+
customBlockDeserializers,
|
|
6
|
+
} from '../BaseSerializationConfig';
|
|
7
|
+
import { PortableTextBlock, SanityDocument, TypedObject } from 'sanity';
|
|
8
|
+
import { SerializedDocument, TranslationLevel } from '../types';
|
|
9
|
+
import schema from './__fixtures__/schema';
|
|
10
|
+
import clone from 'just-clone';
|
|
11
|
+
|
|
12
|
+
export const getSerialized = (
|
|
13
|
+
document: SanityDocument,
|
|
14
|
+
level: TranslationLevel
|
|
15
|
+
): SerializedDocument => {
|
|
16
|
+
const serializer = BaseDocumentSerializer(schema);
|
|
17
|
+
return serializer.serializeDocument(document, level);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const getDeserialized = (
|
|
21
|
+
document: SanityDocument,
|
|
22
|
+
level: TranslationLevel
|
|
23
|
+
): Record<string, any> => {
|
|
24
|
+
const serialized = getSerialized(document, level);
|
|
25
|
+
const deserializer = BaseDocumentDeserializer;
|
|
26
|
+
return deserializer.deserializeDocument(serialized.content);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const getValidFields = (
|
|
30
|
+
field: Record<string, any>
|
|
31
|
+
): Record<string, any> => {
|
|
32
|
+
const invalidFields = ['_type', '_key'];
|
|
33
|
+
return Object.keys(field).filter((key) => !invalidFields.includes(key));
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const toPlainText = (blocks: PortableTextBlock[]): string => {
|
|
37
|
+
return blocks
|
|
38
|
+
.map((block) => {
|
|
39
|
+
if (block._type !== 'block' || !block.children) {
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
return (block.children as Array<any>).map((child) => child.text).join('');
|
|
43
|
+
})
|
|
44
|
+
.join('\n\n');
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const createCustomInnerHTML = (title: string): string =>
|
|
48
|
+
`Custom serializer works and includes title: '${title}'`;
|
|
49
|
+
|
|
50
|
+
const additionalSerializerTypes = {
|
|
51
|
+
//block and top-level tests
|
|
52
|
+
objectField: ({ value }: { value: TypedObject }) => {
|
|
53
|
+
const innerText = createCustomInnerHTML(value.title as string);
|
|
54
|
+
const html = `<div class="${value._type}" id="${value._key ?? value._id}">${innerText}</div>`;
|
|
55
|
+
return html;
|
|
56
|
+
},
|
|
57
|
+
//inline-level tests
|
|
58
|
+
childObjectField: ({ value }: { value: TypedObject }) => {
|
|
59
|
+
const innerText = createCustomInnerHTML(value.title as string);
|
|
60
|
+
const html = `<span class="${value._type}" id="${value._key ?? value._id}">${innerText}</span>`;
|
|
61
|
+
return html;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const tempSerializers = clone(customSerializers);
|
|
66
|
+
tempSerializers.types = {
|
|
67
|
+
...tempSerializers.types,
|
|
68
|
+
...additionalSerializerTypes,
|
|
69
|
+
};
|
|
70
|
+
tempSerializers.marks = {
|
|
71
|
+
annotation: ({ value, markType, children }) => {
|
|
72
|
+
return `<span class="${markType}" id="${value._key}">${children}</span>`;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const addedCustomSerializers = tempSerializers;
|
|
77
|
+
|
|
78
|
+
export const addedDeserializerTypes = {
|
|
79
|
+
objectField: (html: HTMLElement): TypedObject => {
|
|
80
|
+
const title = html.innerHTML.split(':')[1].replace(/'/g, '').trim();
|
|
81
|
+
const _type = html.className;
|
|
82
|
+
const _key = html.id;
|
|
83
|
+
return { title, _type, _key };
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const tempDeserializers = clone(customDeserializers);
|
|
88
|
+
tempDeserializers.types = {
|
|
89
|
+
...tempDeserializers.types,
|
|
90
|
+
...addedDeserializerTypes,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const addedCustomDeserializers = tempDeserializers;
|
|
94
|
+
|
|
95
|
+
export const addedBlockDeserializers = [
|
|
96
|
+
...customBlockDeserializers,
|
|
97
|
+
{
|
|
98
|
+
deserialize(el: HTMLElement): TypedObject | undefined {
|
|
99
|
+
if (!el.className || el.className.toLowerCase() !== 'childobjectfield') {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const title = el.innerHTML.split(':')[1].replace(/'/g, '').trim();
|
|
104
|
+
const _type = el.className;
|
|
105
|
+
const _key = el.id;
|
|
106
|
+
|
|
107
|
+
return { title, _type, _key };
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
deserialize(
|
|
112
|
+
el: HTMLElement,
|
|
113
|
+
//eslint-disable-next-line no-undef -- not picking up NodeListOf/ChildNode
|
|
114
|
+
next: (nodes: NodeListOf<ChildNode>) => any
|
|
115
|
+
): TypedObject | undefined {
|
|
116
|
+
if (!el.className || el.className?.toLowerCase() !== 'annotation') {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const markDef = {
|
|
121
|
+
_key: el.id,
|
|
122
|
+
_type: 'annotation',
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
_type: '__annotation',
|
|
127
|
+
markDef: markDef,
|
|
128
|
+
children: next(el.childNodes),
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
];
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export function attachGTData(
|
|
2
|
+
html: string,
|
|
3
|
+
data: Record<string, any>,
|
|
4
|
+
type: 'markDef'
|
|
5
|
+
): string {
|
|
6
|
+
// Parse the HTML string to find the first element
|
|
7
|
+
const parser = new DOMParser();
|
|
8
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
9
|
+
const firstElement = doc.body.firstElementChild;
|
|
10
|
+
|
|
11
|
+
if (!firstElement) {
|
|
12
|
+
// If no element found, return original HTML
|
|
13
|
+
return html;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Encode the data as base64 JSON
|
|
17
|
+
const encodedData = encode(JSON.stringify({ [type]: data }));
|
|
18
|
+
|
|
19
|
+
// Add the data-gt-internal attribute
|
|
20
|
+
firstElement.setAttribute('data-gt-internal', encodedData);
|
|
21
|
+
|
|
22
|
+
return firstElement.outerHTML;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function detachGTData(html: string): {
|
|
26
|
+
html: string;
|
|
27
|
+
data?: Record<'markDef', Record<string, any>>;
|
|
28
|
+
} {
|
|
29
|
+
// Parse the HTML string to find the first element
|
|
30
|
+
const parser = new DOMParser();
|
|
31
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
32
|
+
const firstElement = doc.body.firstElementChild;
|
|
33
|
+
|
|
34
|
+
if (!firstElement) {
|
|
35
|
+
// If no element found, return original HTML with no data
|
|
36
|
+
return { html };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get the encoded data
|
|
40
|
+
const encodedData = firstElement.getAttribute('data-gt-internal');
|
|
41
|
+
|
|
42
|
+
let extractedData: Record<'markDef', Record<string, any>> | undefined;
|
|
43
|
+
if (encodedData) {
|
|
44
|
+
try {
|
|
45
|
+
// Decode and parse the data
|
|
46
|
+
const decodedData = decode(encodedData);
|
|
47
|
+
extractedData = JSON.parse(decodedData);
|
|
48
|
+
|
|
49
|
+
// Remove the data attribute to clean up the HTML
|
|
50
|
+
firstElement.removeAttribute('data-gt-internal');
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn('Failed to decode GT internal data:', error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
html: firstElement.outerHTML,
|
|
58
|
+
data: extractedData,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Encode a string to base64
|
|
63
|
+
export function encode(data: string): string {
|
|
64
|
+
// Browser path
|
|
65
|
+
const bytes = new TextEncoder().encode(data);
|
|
66
|
+
let binary = '';
|
|
67
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
68
|
+
binary += String.fromCharCode(bytes[i]);
|
|
69
|
+
}
|
|
70
|
+
return btoa(binary);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Decode a base64 string to a string
|
|
74
|
+
export function decode(base64: string): string {
|
|
75
|
+
// Browser path
|
|
76
|
+
const binary = atob(base64);
|
|
77
|
+
const bytes = new Uint8Array(binary.length);
|
|
78
|
+
for (let i = 0; i < binary.length; i++) {
|
|
79
|
+
bytes[i] = binary.charCodeAt(i);
|
|
80
|
+
}
|
|
81
|
+
return new TextDecoder().decode(bytes);
|
|
82
|
+
}
|