sanity-plugin-internationalized-array 3.1.4 → 3.1.6
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.esm.js +161 -22
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +160 -21
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +161 -22
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/cache.ts +129 -1
- package/src/components/DocumentAddButtons.tsx +71 -3
- package/src/components/Feedback.tsx +2 -1
- package/src/components/InternationalizedArray.tsx +2 -2
- package/src/components/InternationalizedArrayContext.tsx +38 -5
- package/src/components/InternationalizedInput.tsx +93 -1
- package/src/components/Preload.tsx +16 -6
- package/src/schema/array.ts +53 -28
package/lib/index.js
CHANGED
|
@@ -20,7 +20,32 @@ function _interopNamespaceCompat(e) {
|
|
|
20
20
|
}), n.default = e, Object.freeze(n);
|
|
21
21
|
}
|
|
22
22
|
var suspend__namespace = /* @__PURE__ */ _interopNamespaceCompat(suspend), equal__default = /* @__PURE__ */ _interopDefaultCompat(equal), get__default = /* @__PURE__ */ _interopDefaultCompat(get);
|
|
23
|
-
const namespace = "sanity-plugin-internationalized-array", version = "v1",
|
|
23
|
+
const namespace = "sanity-plugin-internationalized-array", version = "v1", functionCache = /* @__PURE__ */ new Map(), functionKeyCache = /* @__PURE__ */ new WeakMap(), preloadWithKey = (fn, key) => suspend__namespace.preload(() => fn(), key), clear = () => suspend__namespace.clear([version, namespace]), peek = (selectedValue) => suspend__namespace.peek([version, namespace, selectedValue]), createCacheKey = (selectedValue, workspaceId) => {
|
|
24
|
+
const selectedValueHash = JSON.stringify(selectedValue);
|
|
25
|
+
return workspaceId ? [version, namespace, selectedValueHash, workspaceId] : [version, namespace, selectedValueHash];
|
|
26
|
+
}, getFunctionKey = (fn) => {
|
|
27
|
+
const cachedKey = functionKeyCache.get(fn);
|
|
28
|
+
if (cachedKey)
|
|
29
|
+
return cachedKey;
|
|
30
|
+
const fnStr = fn.toString();
|
|
31
|
+
let hash = 0;
|
|
32
|
+
const maxLength = Math.min(fnStr.length, 100);
|
|
33
|
+
for (let i = 0; i < maxLength; i++) {
|
|
34
|
+
const char = fnStr.charCodeAt(i);
|
|
35
|
+
hash = (hash << 5) - hash + char, hash &= hash;
|
|
36
|
+
}
|
|
37
|
+
const key = `anonymous_${Math.abs(hash)}`;
|
|
38
|
+
return functionKeyCache.set(fn, key), key;
|
|
39
|
+
}, createFunctionCacheKey = (fn, selectedValue, workspaceId) => {
|
|
40
|
+
const functionKey = getFunctionKey(fn), selectedValueHash = JSON.stringify(selectedValue);
|
|
41
|
+
return workspaceId ? `${functionKey}:${selectedValueHash}:${workspaceId}` : `${functionKey}:${selectedValueHash}`;
|
|
42
|
+
}, getFunctionCache = (fn, selectedValue, workspaceId) => {
|
|
43
|
+
const key = createFunctionCacheKey(fn, selectedValue, workspaceId);
|
|
44
|
+
return functionCache.get(key);
|
|
45
|
+
}, setFunctionCache = (fn, selectedValue, languages, workspaceId) => {
|
|
46
|
+
const key = createFunctionCacheKey(fn, selectedValue, workspaceId);
|
|
47
|
+
functionCache.set(key, languages);
|
|
48
|
+
}, MAX_COLUMNS = {
|
|
24
49
|
codeOnly: 5,
|
|
25
50
|
titleOnly: 4,
|
|
26
51
|
titleAndCode: 3
|
|
@@ -106,7 +131,43 @@ function AddButtons(props) {
|
|
|
106
131
|
}
|
|
107
132
|
var AddButtons$1 = react.memo(AddButtons);
|
|
108
133
|
function DocumentAddButtons(props) {
|
|
109
|
-
const { filteredLanguages } = useInternationalizedArrayContext(), value = sanity.isSanityDocument(props.value) ? props.value : void 0, toast = ui.useToast(), { onChange } = structure.useDocumentPane(), documentsToTranslation = getDocumentsToTranslate(value, []),
|
|
134
|
+
const { filteredLanguages } = useInternationalizedArrayContext(), value = sanity.isSanityDocument(props.value) ? props.value : void 0, toast = ui.useToast(), { onChange } = structure.useDocumentPane(), schema = sanity.useSchema(), documentsToTranslation = getDocumentsToTranslate(value, []), getInitialValueForType = react.useCallback(
|
|
135
|
+
(typeName) => {
|
|
136
|
+
var _a;
|
|
137
|
+
if (!typeName) return;
|
|
138
|
+
const match = typeName.match(/^internationalizedArray(.+)Value$/);
|
|
139
|
+
if (!match) return;
|
|
140
|
+
const baseTypeName = match[1].charAt(0).toLowerCase() + match[1].slice(1), arrayBasedTypes = [
|
|
141
|
+
"body",
|
|
142
|
+
"htmlContent",
|
|
143
|
+
"blockContent",
|
|
144
|
+
"portableText"
|
|
145
|
+
];
|
|
146
|
+
if (arrayBasedTypes.includes(baseTypeName))
|
|
147
|
+
return [];
|
|
148
|
+
try {
|
|
149
|
+
const schemaType = schema.get(typeName);
|
|
150
|
+
if (schemaType) {
|
|
151
|
+
const valueField = (_a = schemaType == null ? void 0 : schemaType.fields) == null ? void 0 : _a.find(
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
|
+
(f) => f.name === "value"
|
|
154
|
+
);
|
|
155
|
+
if (valueField) {
|
|
156
|
+
const fieldType = valueField.type;
|
|
157
|
+
if ((fieldType == null ? void 0 : fieldType.jsonType) === "array" || (fieldType == null ? void 0 : fieldType.name) === "array" || (fieldType == null ? void 0 : fieldType.type) === "array" || (fieldType == null ? void 0 : fieldType.of) !== void 0 || arrayBasedTypes.includes(fieldType == null ? void 0 : fieldType.name))
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.warn(
|
|
163
|
+
"Could not determine field type from schema:",
|
|
164
|
+
typeName,
|
|
165
|
+
error
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
[schema]
|
|
170
|
+
), handleDocumentButtonClick = react.useCallback(
|
|
110
171
|
async (event) => {
|
|
111
172
|
const languageId = event.currentTarget.value;
|
|
112
173
|
if (!languageId) {
|
|
@@ -132,12 +193,13 @@ function DocumentAddButtons(props) {
|
|
|
132
193
|
}
|
|
133
194
|
const patches = [];
|
|
134
195
|
for (const toTranslate of removeDuplicates) {
|
|
135
|
-
const path = toTranslate.path, ifMissing = sanity.setIfMissing([], path), insertValue = sanity.insert(
|
|
196
|
+
const path = toTranslate.path, initialValue = getInitialValueForType(toTranslate._type), ifMissing = sanity.setIfMissing([], path), insertValue = sanity.insert(
|
|
136
197
|
[
|
|
137
198
|
{
|
|
138
199
|
_key: languageId,
|
|
139
200
|
_type: toTranslate._type,
|
|
140
|
-
value:
|
|
201
|
+
value: initialValue
|
|
202
|
+
// Use the determined initial value instead of undefined
|
|
141
203
|
}
|
|
142
204
|
],
|
|
143
205
|
"after",
|
|
@@ -147,7 +209,7 @@ function DocumentAddButtons(props) {
|
|
|
147
209
|
}
|
|
148
210
|
onChange(sanity.PatchEvent.from(patches.flat()));
|
|
149
211
|
},
|
|
150
|
-
[documentsToTranslation, onChange, toast]
|
|
212
|
+
[documentsToTranslation, getInitialValueForType, onChange, toast]
|
|
151
213
|
);
|
|
152
214
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
|
|
153
215
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: "Add translation to internationalized fields" }) }),
|
|
@@ -193,10 +255,36 @@ function InternationalizedArrayProvider(props) {
|
|
|
193
255
|
const { internationalizedArray: internationalizedArray2 } = props, client = sanity.useClient({ apiVersion: internationalizedArray2.apiVersion }), workspace = sanity.useWorkspace(), { formState } = structure.useDocumentPane(), deferredDocument = react.useDeferredValue(formState == null ? void 0 : formState.value), selectedValue = react.useMemo(
|
|
194
256
|
() => getSelectedValue(internationalizedArray2.select, deferredDocument),
|
|
195
257
|
[internationalizedArray2.select, deferredDocument]
|
|
258
|
+
), workspaceId = react.useMemo(() => {
|
|
259
|
+
if (workspace != null && workspace.name)
|
|
260
|
+
return workspace.name;
|
|
261
|
+
const workspaceKey = {
|
|
262
|
+
name: workspace == null ? void 0 : workspace.name,
|
|
263
|
+
title: workspace == null ? void 0 : workspace.title
|
|
264
|
+
// Add other stable properties as needed
|
|
265
|
+
};
|
|
266
|
+
return JSON.stringify(workspaceKey);
|
|
267
|
+
}, [workspace]), cacheKey = react.useMemo(
|
|
268
|
+
() => createCacheKey(selectedValue, workspaceId),
|
|
269
|
+
[selectedValue, workspaceId]
|
|
196
270
|
), languages = Array.isArray(internationalizedArray2.languages) ? internationalizedArray2.languages : suspend.suspend(
|
|
197
271
|
// eslint-disable-next-line require-await
|
|
198
|
-
async () =>
|
|
199
|
-
|
|
272
|
+
async () => {
|
|
273
|
+
if (typeof internationalizedArray2.languages == "function") {
|
|
274
|
+
const result = await internationalizedArray2.languages(
|
|
275
|
+
client,
|
|
276
|
+
selectedValue
|
|
277
|
+
);
|
|
278
|
+
return setFunctionCache(
|
|
279
|
+
internationalizedArray2.languages,
|
|
280
|
+
selectedValue,
|
|
281
|
+
result,
|
|
282
|
+
workspaceId
|
|
283
|
+
), result;
|
|
284
|
+
}
|
|
285
|
+
return internationalizedArray2.languages;
|
|
286
|
+
},
|
|
287
|
+
cacheKey,
|
|
200
288
|
{ equal: equal__default.default }
|
|
201
289
|
), { selectedLanguageIds, options: languageFilterOptions } = languageFilter.useLanguageFilterStudioContext(), filteredLanguages = react.useMemo(() => {
|
|
202
290
|
const documentType = deferredDocument ? deferredDocument._type : void 0;
|
|
@@ -238,10 +326,13 @@ function InternationalizedField(props) {
|
|
|
238
326
|
})) : customProps.renderDefault(customProps);
|
|
239
327
|
}
|
|
240
328
|
var Preload = react.memo(function(props) {
|
|
241
|
-
const client = sanity.useClient({ apiVersion: props.apiVersion });
|
|
242
|
-
return Array.isArray(peek({})) ||
|
|
243
|
-
|
|
244
|
-
|
|
329
|
+
const client = sanity.useClient({ apiVersion: props.apiVersion }), cacheKey = createCacheKey({});
|
|
330
|
+
return Array.isArray(peek({})) || preloadWithKey(async () => {
|
|
331
|
+
if (Array.isArray(props.languages))
|
|
332
|
+
return props.languages;
|
|
333
|
+
const result = await props.languages(client, {});
|
|
334
|
+
return setFunctionCache(props.languages, {}, result), result;
|
|
335
|
+
}, cacheKey), null;
|
|
245
336
|
});
|
|
246
337
|
function checkAllLanguagesArePresent(languages, value) {
|
|
247
338
|
const filteredLanguageIds = languages.map((l) => l.id), languagesInUseIds = value ? value.map((v) => v._key) : [];
|
|
@@ -550,24 +641,48 @@ var __defProp$4 = Object.defineProperty, __defProps$3 = Object.defineProperties,
|
|
|
550
641
|
],
|
|
551
642
|
// @ts-expect-error - fix typings
|
|
552
643
|
validation: (rule) => rule.custom(async (value, context) => {
|
|
553
|
-
|
|
644
|
+
var _a;
|
|
645
|
+
if (!value || value.length === 0 || value.length === 1 && !((_a = value[0]) != null && _a._key))
|
|
554
646
|
return !0;
|
|
555
647
|
const selectedValue = getSelectedValue(select, context.document), client = context.getClient({ apiVersion });
|
|
556
648
|
let contextLanguages = [];
|
|
557
649
|
const languagesFieldOption = getLanguagesFieldOption(context == null ? void 0 : context.type);
|
|
558
|
-
if (Array.isArray(languagesFieldOption)
|
|
650
|
+
if (Array.isArray(languagesFieldOption))
|
|
651
|
+
contextLanguages = languagesFieldOption;
|
|
652
|
+
else if (Array.isArray(peek(selectedValue)))
|
|
653
|
+
contextLanguages = peek(selectedValue) || [];
|
|
654
|
+
else if (typeof languagesFieldOption == "function") {
|
|
655
|
+
const cachedLanguages = getFunctionCache(
|
|
656
|
+
languagesFieldOption,
|
|
657
|
+
selectedValue
|
|
658
|
+
);
|
|
659
|
+
if (Array.isArray(cachedLanguages))
|
|
660
|
+
contextLanguages = cachedLanguages;
|
|
661
|
+
else {
|
|
662
|
+
const suspendCachedLanguages = peek(selectedValue);
|
|
663
|
+
Array.isArray(suspendCachedLanguages) ? contextLanguages = suspendCachedLanguages : (contextLanguages = await languagesFieldOption(
|
|
664
|
+
client,
|
|
665
|
+
selectedValue
|
|
666
|
+
), setFunctionCache(
|
|
667
|
+
languagesFieldOption,
|
|
668
|
+
selectedValue,
|
|
669
|
+
contextLanguages
|
|
670
|
+
));
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (value && value.length > contextLanguages.length)
|
|
559
674
|
return `Cannot be more than ${contextLanguages.length === 1 ? "1 item" : `${contextLanguages.length} items`}`;
|
|
560
|
-
const
|
|
561
|
-
(item) =>
|
|
562
|
-
)
|
|
675
|
+
const languageIds = new Set(contextLanguages.map((lang) => lang.id)), nonLanguageKeys = value.filter(
|
|
676
|
+
(item) => (item == null ? void 0 : item._key) && !languageIds.has(item._key)
|
|
677
|
+
);
|
|
563
678
|
if (nonLanguageKeys.length)
|
|
564
679
|
return {
|
|
565
680
|
message: "Array item keys must be valid languages registered to the field type",
|
|
566
681
|
paths: nonLanguageKeys.map((item) => [{ _key: item._key }])
|
|
567
682
|
};
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
683
|
+
const seenKeys = /* @__PURE__ */ new Set(), duplicateValues = [];
|
|
684
|
+
for (const item of value)
|
|
685
|
+
item != null && item._key && (seenKeys.has(item._key) ? duplicateValues.push(item) : seenKeys.add(item._key));
|
|
571
686
|
return duplicateValues.length ? {
|
|
572
687
|
message: "There can only be one field per language",
|
|
573
688
|
paths: duplicateValues.map((item) => [{ _key: item._key }])
|
|
@@ -595,6 +710,28 @@ var __defProp$3 = Object.defineProperty, __defProps$2 = Object.defineProperties,
|
|
|
595
710
|
function InternationalizedInput(props) {
|
|
596
711
|
const parentValue = sanity.useFormValue(
|
|
597
712
|
props.path.slice(0, -1)
|
|
713
|
+
), originalOnChange = props.inputProps.onChange, wrappedOnChange = react.useCallback(
|
|
714
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
715
|
+
(patches) => {
|
|
716
|
+
var _a;
|
|
717
|
+
if (!Array.isArray(patches))
|
|
718
|
+
return originalOnChange(patches);
|
|
719
|
+
const valueField = (_a = props.value) == null ? void 0 : _a.value;
|
|
720
|
+
if ((valueField == null || Array.isArray(valueField) && valueField.length === 0) && patches.some((patch) => !patch || typeof patch != "object" ? !1 : patch.type === "insert" && patch.path && Array.isArray(patch.path) && patch.path.length > 0 ? patch.path[0] === "value" || typeof patch.path[0] == "number" : !1)) {
|
|
721
|
+
const initPatch = valueField === void 0 ? { type: "setIfMissing", path: ["value"], value: [] } : null, fixedPatches = patches.map((patch) => {
|
|
722
|
+
if (!patch || typeof patch != "object")
|
|
723
|
+
return patch;
|
|
724
|
+
if (patch.type === "insert" && patch.path && Array.isArray(patch.path)) {
|
|
725
|
+
const fixedPath = patch.path[0] === "value" ? patch.path : ["value", ...patch.path];
|
|
726
|
+
return __spreadProps$2(__spreadValues$3({}, patch), { path: fixedPath });
|
|
727
|
+
}
|
|
728
|
+
return patch;
|
|
729
|
+
}), allPatches = initPatch ? [initPatch, ...fixedPatches] : fixedPatches;
|
|
730
|
+
return originalOnChange(allPatches);
|
|
731
|
+
}
|
|
732
|
+
return originalOnChange(patches);
|
|
733
|
+
},
|
|
734
|
+
[props.value, originalOnChange]
|
|
598
735
|
), inlineProps = __spreadProps$2(__spreadValues$3({}, props.inputProps), {
|
|
599
736
|
// This is the magic that makes inline editing work?
|
|
600
737
|
members: props.inputProps.members.filter(
|
|
@@ -602,7 +739,9 @@ function InternationalizedInput(props) {
|
|
|
602
739
|
),
|
|
603
740
|
// This just overrides the type
|
|
604
741
|
// Remove this as it shouldn't be necessary?
|
|
605
|
-
value: props.value
|
|
742
|
+
value: props.value,
|
|
743
|
+
// Use our wrapped onChange handler
|
|
744
|
+
onChange: wrappedOnChange
|
|
606
745
|
}), { validation, value, onChange, readOnly } = inlineProps, { languages, languageDisplay, defaultLanguages } = useInternationalizedArrayContext(), languageKeysInUse = react.useMemo(
|
|
607
746
|
() => {
|
|
608
747
|
var _a;
|
|
@@ -652,7 +791,7 @@ function InternationalizedInput(props) {
|
|
|
652
791
|
}
|
|
653
792
|
) }),
|
|
654
793
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", gap: 2, children: [
|
|
655
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { flex: 1, tone: "inherit", children: props.inputProps.renderInput(
|
|
794
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { flex: 1, tone: "inherit", children: props.inputProps.renderInput(inlineProps) }),
|
|
656
795
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Card, { tone: "inherit", children: isDefault ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
657
796
|
ui.Tooltip,
|
|
658
797
|
{
|