@payloadcms/ui 3.80.0-internal-debug.daef79f → 3.80.0-internal-debug.cd99b1f
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/dist/elements/ClipboardAction/mergeFormStateFromClipboard.d.ts.map +1 -1
- package/dist/elements/ClipboardAction/mergeFormStateFromClipboard.js +49 -1
- package/dist/elements/ClipboardAction/mergeFormStateFromClipboard.js.map +1 -1
- package/dist/elements/ClipboardAction/mergeFormStateFromClipboard.spec.js +389 -0
- package/dist/elements/ClipboardAction/mergeFormStateFromClipboard.spec.js.map +1 -0
- package/dist/elements/Toasts/fieldErrors.d.ts +4 -0
- package/dist/elements/Toasts/fieldErrors.d.ts.map +1 -1
- package/dist/elements/Toasts/fieldErrors.js +8 -6
- package/dist/elements/Toasts/fieldErrors.js.map +1 -1
- package/dist/elements/Toasts/fieldErrors.spec.js +60 -0
- package/dist/elements/Toasts/fieldErrors.spec.js.map +1 -0
- package/dist/elements/WhereBuilder/Condition/Relationship/index.d.ts.map +1 -1
- package/dist/elements/WhereBuilder/Condition/Relationship/index.js +14 -10
- package/dist/elements/WhereBuilder/Condition/Relationship/index.js.map +1 -1
- package/dist/elements/WhereBuilder/field-types.d.ts.map +1 -1
- package/dist/elements/WhereBuilder/field-types.js +5 -10
- package/dist/elements/WhereBuilder/field-types.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeFormStateFromClipboard.d.ts","sourceRoot":"","sources":["../../../src/elements/ClipboardAction/mergeFormStateFromClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"mergeFormStateFromClipboard.d.ts","sourceRoot":"","sources":["../../../src/elements/ClipboardAction/mergeFormStateFromClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAIpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAIpD,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,SAAS,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,8BAyBA;AAED,wBAAgB,2BAA2B,CAAC,EAC1C,iBAAiB,EAAE,aAAa,EAChC,SAAS,EACT,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,iBAAiB,EAAE,kBAAkB,CAAA;IACrC,SAAS,EAAE,SAAS,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,aAwIA"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import ObjectIdImport from 'bson-objectid';
|
|
2
|
+
const ObjectId = 'default' in ObjectIdImport ? ObjectIdImport.default : ObjectIdImport;
|
|
1
3
|
export function reduceFormStateByPath({ formState, path, rowIndex }) {
|
|
2
4
|
const filteredState = {};
|
|
3
5
|
const prefix = typeof rowIndex !== 'number' ? path : `${path}.${rowIndex}`;
|
|
@@ -43,7 +45,7 @@ export function mergeFormStateFromClipboard({ dataFromClipboard: clipboardData,
|
|
|
43
45
|
}
|
|
44
46
|
if (fromRowToField) {
|
|
45
47
|
const lastRenderedPath = `${path}.0`;
|
|
46
|
-
const rowIDFromClipboard = dataFromClipboard[`${pathToReplace}.id`]
|
|
48
|
+
const rowIDFromClipboard = dataFromClipboard[`${pathToReplace}.id`]?.value;
|
|
47
49
|
const hasRows = formState[path].rows?.length;
|
|
48
50
|
formState[path].rows = [
|
|
49
51
|
{
|
|
@@ -62,6 +64,8 @@ export function mergeFormStateFromClipboard({ dataFromClipboard: clipboardData,
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
}
|
|
67
|
+
// Map to track old IDs to new IDs for regenerating nested IDs
|
|
68
|
+
const idReplacements = new Map();
|
|
65
69
|
for(const clipboardPath in dataFromClipboard){
|
|
66
70
|
// Pasting a row id, skip overwriting
|
|
67
71
|
if (!pasteIntoField && clipboardPath.endsWith('.id') || !clipboardPath.startsWith(pathToReplace)) {
|
|
@@ -70,12 +74,56 @@ export function mergeFormStateFromClipboard({ dataFromClipboard: clipboardData,
|
|
|
70
74
|
const newPath = clipboardPath.replace(pathToReplace, targetSegment);
|
|
71
75
|
const customComponents = isArray ? formState[newPath]?.customComponents : undefined;
|
|
72
76
|
const validate = isArray ? formState[newPath]?.validate : undefined;
|
|
77
|
+
// If this is an ID field, generate a new ID to prevent duplicates
|
|
78
|
+
if (clipboardPath.endsWith('.id') && dataFromClipboard[clipboardPath]?.value) {
|
|
79
|
+
const oldID = dataFromClipboard[clipboardPath].value;
|
|
80
|
+
if (typeof oldID === 'string' && ObjectId.isValid(oldID)) {
|
|
81
|
+
const newID = new ObjectId().toHexString();
|
|
82
|
+
idReplacements.set(clipboardPath, newID);
|
|
83
|
+
formState[newPath] = {
|
|
84
|
+
customComponents,
|
|
85
|
+
validate,
|
|
86
|
+
...dataFromClipboard[clipboardPath],
|
|
87
|
+
initialValue: newID,
|
|
88
|
+
value: newID
|
|
89
|
+
};
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
73
93
|
formState[newPath] = {
|
|
74
94
|
customComponents,
|
|
75
95
|
validate,
|
|
76
96
|
...dataFromClipboard[clipboardPath]
|
|
77
97
|
};
|
|
78
98
|
}
|
|
99
|
+
// Update parent field rows with new IDs
|
|
100
|
+
for (const [clipboardPath, newID] of idReplacements){
|
|
101
|
+
const relativePath = clipboardPath.replace(`${pathToReplace}.`, '');
|
|
102
|
+
const segments = relativePath.split('.');
|
|
103
|
+
if (segments.length >= 2) {
|
|
104
|
+
const rowIndex = parseInt(segments[segments.length - 2], 10);
|
|
105
|
+
const parentFieldPath = segments.slice(0, segments.length - 2).join('.');
|
|
106
|
+
const fullParentPath = parentFieldPath ? `${targetSegment}.${parentFieldPath}` : targetSegment;
|
|
107
|
+
if (formState[fullParentPath] && Array.isArray(formState[fullParentPath].rows)) {
|
|
108
|
+
const parentRows = formState[fullParentPath].rows;
|
|
109
|
+
if (!isNaN(rowIndex) && parentRows[rowIndex]) {
|
|
110
|
+
parentRows[rowIndex].id = newID;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else if (segments.length === 1 && segments[0] === 'id') {
|
|
114
|
+
// Top-level block ID - extract field path and row index from targetSegment
|
|
115
|
+
const targetParts = targetSegment.split('.');
|
|
116
|
+
const lastPart = targetParts[targetParts.length - 1];
|
|
117
|
+
const rowIndexFromTarget = !isNaN(parseInt(lastPart, 10)) ? parseInt(lastPart, 10) : 0;
|
|
118
|
+
const fieldPath = !isNaN(parseInt(lastPart, 10)) ? targetParts.slice(0, -1).join('.') : targetSegment;
|
|
119
|
+
if (formState[fieldPath] && Array.isArray(formState[fieldPath].rows)) {
|
|
120
|
+
const rows = formState[fieldPath].rows;
|
|
121
|
+
if (rows[rowIndexFromTarget]) {
|
|
122
|
+
rows[rowIndexFromTarget].id = newID;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
79
127
|
return formState;
|
|
80
128
|
}
|
|
81
129
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/elements/ClipboardAction/mergeFormStateFromClipboard.ts"],"sourcesContent":["import type { FieldState, FormState } from 'payload'\n\nimport type { ClipboardPasteData } from './types.js'\n\nexport function reduceFormStateByPath({\n formState,\n path,\n rowIndex,\n}: {\n formState: FormState\n path: string\n rowIndex?: number\n}) {\n const filteredState: Record<string, FieldState> = {}\n const prefix = typeof rowIndex !== 'number' ? path : `${path}.${rowIndex}`\n\n for (const key in formState) {\n if (!key.startsWith(prefix)) {\n continue\n }\n\n const { customComponents: _, validate: __, ...field } = formState[key]\n\n if (Array.isArray(field.rows)) {\n field.rows = field.rows.map((row) => {\n if (!row || typeof row !== 'object') {\n return row\n }\n const { customComponents: _, ...serializableRow } = row\n return serializableRow\n })\n }\n\n filteredState[key] = field\n }\n\n return filteredState\n}\n\nexport function mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path,\n rowIndex,\n}: {\n dataFromClipboard: ClipboardPasteData\n formState: FormState\n path: string\n rowIndex?: number\n}) {\n const {\n type: typeFromClipboard,\n data: dataFromClipboard,\n path: pathFromClipboard,\n rowIndex: rowIndexFromClipboard,\n } = clipboardData\n\n const copyFromField = typeof rowIndexFromClipboard !== 'number'\n const pasteIntoField = typeof rowIndex !== 'number'\n const fromRowToField = !copyFromField && pasteIntoField\n const isArray = typeFromClipboard === 'array'\n\n let pathToReplace: string\n if (copyFromField && pasteIntoField) {\n pathToReplace = pathFromClipboard\n } else if (copyFromField) {\n pathToReplace = `${pathFromClipboard}.${rowIndex}`\n } else {\n pathToReplace = `${pathFromClipboard}.${rowIndexFromClipboard}`\n }\n\n let targetSegment: string\n if (!pasteIntoField) {\n targetSegment = `${path}.${rowIndex}`\n } else if (fromRowToField) {\n targetSegment = `${path}.0`\n } else {\n targetSegment = path\n }\n\n if (fromRowToField) {\n const lastRenderedPath = `${path}.0`\n const rowIDFromClipboard = dataFromClipboard[`${pathToReplace}.id`].value as string\n const hasRows = formState[path].rows?.length\n\n formState[path].rows = [\n {\n ...(hasRows && isArray ? formState[path].rows[0] : {}),\n id: rowIDFromClipboard,\n isLoading: false,\n lastRenderedPath,\n },\n ]\n formState[path].value = 1\n formState[path].initialValue = 1\n formState[path].disableFormData = true\n\n for (const fieldPath in formState) {\n if (\n fieldPath !== path &&\n !fieldPath.startsWith(lastRenderedPath) &&\n fieldPath.startsWith(path)\n ) {\n delete formState[fieldPath]\n }\n }\n }\n\n for (const clipboardPath in dataFromClipboard) {\n // Pasting a row id, skip overwriting\n if (\n (!pasteIntoField && clipboardPath.endsWith('.id')) ||\n !clipboardPath.startsWith(pathToReplace)\n ) {\n continue\n }\n\n const newPath = clipboardPath.replace(pathToReplace, targetSegment)\n\n const customComponents = isArray ? formState[newPath]?.customComponents : undefined\n const validate = isArray ? formState[newPath]?.validate : undefined\n\n formState[newPath] = {\n customComponents,\n validate,\n ...dataFromClipboard[clipboardPath],\n }\n }\n\n return formState\n}\n"],"names":["reduceFormStateByPath","formState","path","rowIndex","filteredState","prefix","key","startsWith","customComponents","_","validate","__","field","Array","isArray","rows","map","row","serializableRow","mergeFormStateFromClipboard","dataFromClipboard","clipboardData","type","typeFromClipboard","data","pathFromClipboard","rowIndexFromClipboard","copyFromField","pasteIntoField","fromRowToField","pathToReplace","targetSegment","lastRenderedPath","rowIDFromClipboard","value","hasRows","length","id","isLoading","initialValue","disableFormData","fieldPath","clipboardPath","endsWith","newPath","replace","undefined"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/elements/ClipboardAction/mergeFormStateFromClipboard.ts"],"sourcesContent":["import type { FieldState, FormState } from 'payload'\n\nimport ObjectIdImport from 'bson-objectid'\n\nimport type { ClipboardPasteData } from './types.js'\n\nconst ObjectId = 'default' in ObjectIdImport ? ObjectIdImport.default : ObjectIdImport\n\nexport function reduceFormStateByPath({\n formState,\n path,\n rowIndex,\n}: {\n formState: FormState\n path: string\n rowIndex?: number\n}) {\n const filteredState: Record<string, FieldState> = {}\n const prefix = typeof rowIndex !== 'number' ? path : `${path}.${rowIndex}`\n\n for (const key in formState) {\n if (!key.startsWith(prefix)) {\n continue\n }\n\n const { customComponents: _, validate: __, ...field } = formState[key]\n\n if (Array.isArray(field.rows)) {\n field.rows = field.rows.map((row) => {\n if (!row || typeof row !== 'object') {\n return row\n }\n const { customComponents: _, ...serializableRow } = row\n return serializableRow\n })\n }\n\n filteredState[key] = field\n }\n\n return filteredState\n}\n\nexport function mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path,\n rowIndex,\n}: {\n dataFromClipboard: ClipboardPasteData\n formState: FormState\n path: string\n rowIndex?: number\n}) {\n const {\n type: typeFromClipboard,\n data: dataFromClipboard,\n path: pathFromClipboard,\n rowIndex: rowIndexFromClipboard,\n } = clipboardData\n\n const copyFromField = typeof rowIndexFromClipboard !== 'number'\n const pasteIntoField = typeof rowIndex !== 'number'\n const fromRowToField = !copyFromField && pasteIntoField\n const isArray = typeFromClipboard === 'array'\n\n let pathToReplace: string\n if (copyFromField && pasteIntoField) {\n pathToReplace = pathFromClipboard\n } else if (copyFromField) {\n pathToReplace = `${pathFromClipboard}.${rowIndex}`\n } else {\n pathToReplace = `${pathFromClipboard}.${rowIndexFromClipboard}`\n }\n\n let targetSegment: string\n if (!pasteIntoField) {\n targetSegment = `${path}.${rowIndex}`\n } else if (fromRowToField) {\n targetSegment = `${path}.0`\n } else {\n targetSegment = path\n }\n\n if (fromRowToField) {\n const lastRenderedPath = `${path}.0`\n const rowIDFromClipboard = dataFromClipboard[`${pathToReplace}.id`]?.value as string\n const hasRows = formState[path].rows?.length\n\n formState[path].rows = [\n {\n ...(hasRows && isArray ? formState[path].rows[0] : {}),\n id: rowIDFromClipboard,\n isLoading: false,\n lastRenderedPath,\n },\n ]\n formState[path].value = 1\n formState[path].initialValue = 1\n formState[path].disableFormData = true\n\n for (const fieldPath in formState) {\n if (\n fieldPath !== path &&\n !fieldPath.startsWith(lastRenderedPath) &&\n fieldPath.startsWith(path)\n ) {\n delete formState[fieldPath]\n }\n }\n }\n\n // Map to track old IDs to new IDs for regenerating nested IDs\n const idReplacements: Map<string, string> = new Map()\n\n for (const clipboardPath in dataFromClipboard) {\n // Pasting a row id, skip overwriting\n if (\n (!pasteIntoField && clipboardPath.endsWith('.id')) ||\n !clipboardPath.startsWith(pathToReplace)\n ) {\n continue\n }\n\n const newPath = clipboardPath.replace(pathToReplace, targetSegment)\n\n const customComponents = isArray ? formState[newPath]?.customComponents : undefined\n const validate = isArray ? formState[newPath]?.validate : undefined\n\n // If this is an ID field, generate a new ID to prevent duplicates\n if (clipboardPath.endsWith('.id') && dataFromClipboard[clipboardPath]?.value) {\n const oldID = dataFromClipboard[clipboardPath].value as string\n if (typeof oldID === 'string' && ObjectId.isValid(oldID)) {\n const newID = new ObjectId().toHexString()\n idReplacements.set(clipboardPath, newID)\n\n formState[newPath] = {\n customComponents,\n validate,\n ...dataFromClipboard[clipboardPath],\n initialValue: newID,\n value: newID,\n }\n continue\n }\n }\n\n formState[newPath] = {\n customComponents,\n validate,\n ...dataFromClipboard[clipboardPath],\n }\n }\n\n // Update parent field rows with new IDs\n for (const [clipboardPath, newID] of idReplacements) {\n const relativePath = clipboardPath.replace(`${pathToReplace}.`, '')\n const segments = relativePath.split('.')\n\n if (segments.length >= 2) {\n const rowIndex = parseInt(segments[segments.length - 2], 10)\n const parentFieldPath = segments.slice(0, segments.length - 2).join('.')\n const fullParentPath = parentFieldPath ? `${targetSegment}.${parentFieldPath}` : targetSegment\n\n if (formState[fullParentPath] && Array.isArray(formState[fullParentPath].rows)) {\n const parentRows = formState[fullParentPath].rows\n if (!isNaN(rowIndex) && parentRows[rowIndex]) {\n parentRows[rowIndex].id = newID\n }\n }\n } else if (segments.length === 1 && segments[0] === 'id') {\n // Top-level block ID - extract field path and row index from targetSegment\n const targetParts = targetSegment.split('.')\n const lastPart = targetParts[targetParts.length - 1]\n const rowIndexFromTarget = !isNaN(parseInt(lastPart, 10)) ? parseInt(lastPart, 10) : 0\n const fieldPath = !isNaN(parseInt(lastPart, 10))\n ? targetParts.slice(0, -1).join('.')\n : targetSegment\n\n if (formState[fieldPath] && Array.isArray(formState[fieldPath].rows)) {\n const rows = formState[fieldPath].rows\n if (rows[rowIndexFromTarget]) {\n rows[rowIndexFromTarget].id = newID\n }\n }\n }\n }\n\n return formState\n}\n"],"names":["ObjectIdImport","ObjectId","default","reduceFormStateByPath","formState","path","rowIndex","filteredState","prefix","key","startsWith","customComponents","_","validate","__","field","Array","isArray","rows","map","row","serializableRow","mergeFormStateFromClipboard","dataFromClipboard","clipboardData","type","typeFromClipboard","data","pathFromClipboard","rowIndexFromClipboard","copyFromField","pasteIntoField","fromRowToField","pathToReplace","targetSegment","lastRenderedPath","rowIDFromClipboard","value","hasRows","length","id","isLoading","initialValue","disableFormData","fieldPath","idReplacements","Map","clipboardPath","endsWith","newPath","replace","undefined","oldID","isValid","newID","toHexString","set","relativePath","segments","split","parseInt","parentFieldPath","slice","join","fullParentPath","parentRows","isNaN","targetParts","lastPart","rowIndexFromTarget"],"mappings":"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"}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import ObjectIdImport from 'bson-objectid';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { mergeFormStateFromClipboard } from './mergeFormStateFromClipboard.js';
|
|
4
|
+
const ObjectId = 'default' in ObjectIdImport ? ObjectIdImport.default : ObjectIdImport;
|
|
5
|
+
describe('mergeFormStateFromClipboard', ()=>{
|
|
6
|
+
describe('block ID regeneration', ()=>{
|
|
7
|
+
it('should generate new IDs when pasting blocks to prevent duplicates', ()=>{
|
|
8
|
+
const copiedBlockID = new ObjectId().toHexString();
|
|
9
|
+
const formState = {
|
|
10
|
+
layout: {
|
|
11
|
+
valid: true,
|
|
12
|
+
value: 0,
|
|
13
|
+
initialValue: 0,
|
|
14
|
+
rows: []
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const clipboardData = {
|
|
18
|
+
type: 'blocks',
|
|
19
|
+
path: 'layout',
|
|
20
|
+
blocks: [],
|
|
21
|
+
data: {
|
|
22
|
+
'layout.0.id': {
|
|
23
|
+
value: copiedBlockID,
|
|
24
|
+
valid: true
|
|
25
|
+
},
|
|
26
|
+
'layout.0.blockType': {
|
|
27
|
+
value: 'content',
|
|
28
|
+
valid: true
|
|
29
|
+
},
|
|
30
|
+
'layout.0.text': {
|
|
31
|
+
value: 'test content',
|
|
32
|
+
valid: true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
rowIndex: 0
|
|
36
|
+
};
|
|
37
|
+
const result = mergeFormStateFromClipboard({
|
|
38
|
+
dataFromClipboard: clipboardData,
|
|
39
|
+
formState,
|
|
40
|
+
path: 'layout'
|
|
41
|
+
});
|
|
42
|
+
// Check that a new ID was generated
|
|
43
|
+
expect(result['layout.0.id']).toBeDefined();
|
|
44
|
+
expect(result['layout.0.id'].value).toBeDefined();
|
|
45
|
+
expect(result['layout.0.id'].value).not.toEqual(copiedBlockID);
|
|
46
|
+
expect(ObjectId.isValid(result['layout.0.id'].value)).toBe(true);
|
|
47
|
+
// Check that the row metadata also has the new ID
|
|
48
|
+
expect(result.layout.rows).toHaveLength(1);
|
|
49
|
+
expect(result.layout.rows?.[0]?.id).not.toEqual(copiedBlockID);
|
|
50
|
+
expect(result.layout.rows?.[0]?.id).toEqual(result['layout.0.id'].value);
|
|
51
|
+
});
|
|
52
|
+
it('should generate new IDs for nested blocks', ()=>{
|
|
53
|
+
const copiedBlockID = new ObjectId().toHexString();
|
|
54
|
+
const copiedNestedBlockID = new ObjectId().toHexString();
|
|
55
|
+
const formState = {
|
|
56
|
+
layout: {
|
|
57
|
+
valid: true,
|
|
58
|
+
value: 0,
|
|
59
|
+
initialValue: 0,
|
|
60
|
+
rows: []
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const clipboardData = {
|
|
64
|
+
type: 'blocks',
|
|
65
|
+
path: 'layout',
|
|
66
|
+
blocks: [],
|
|
67
|
+
data: {
|
|
68
|
+
'layout.0.id': {
|
|
69
|
+
value: copiedBlockID,
|
|
70
|
+
valid: true
|
|
71
|
+
},
|
|
72
|
+
'layout.0.blockType': {
|
|
73
|
+
value: 'container',
|
|
74
|
+
valid: true
|
|
75
|
+
},
|
|
76
|
+
'layout.0.subBlocks': {
|
|
77
|
+
value: 1,
|
|
78
|
+
valid: true,
|
|
79
|
+
rows: [
|
|
80
|
+
{
|
|
81
|
+
id: copiedNestedBlockID
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
'layout.0.subBlocks.0.id': {
|
|
86
|
+
value: copiedNestedBlockID,
|
|
87
|
+
valid: true
|
|
88
|
+
},
|
|
89
|
+
'layout.0.subBlocks.0.blockType': {
|
|
90
|
+
value: 'content',
|
|
91
|
+
valid: true
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
rowIndex: 0
|
|
95
|
+
};
|
|
96
|
+
const result = mergeFormStateFromClipboard({
|
|
97
|
+
dataFromClipboard: clipboardData,
|
|
98
|
+
formState,
|
|
99
|
+
path: 'layout'
|
|
100
|
+
});
|
|
101
|
+
// Check that parent block got new ID
|
|
102
|
+
expect(result['layout.0.id'].value).not.toEqual(copiedBlockID);
|
|
103
|
+
expect(ObjectId.isValid(result['layout.0.id'].value)).toBe(true);
|
|
104
|
+
// Check that nested block got new ID
|
|
105
|
+
expect(result['layout.0.subBlocks.0.id'].value).not.toEqual(copiedNestedBlockID);
|
|
106
|
+
expect(ObjectId.isValid(result['layout.0.subBlocks.0.id'].value)).toBe(true);
|
|
107
|
+
// Check that parent and nested IDs are different
|
|
108
|
+
expect(result['layout.0.id'].value).not.toEqual(result['layout.0.subBlocks.0.id'].value);
|
|
109
|
+
// Check that parent row metadata has new ID
|
|
110
|
+
expect(result.layout.rows?.[0]?.id).toEqual(result['layout.0.id'].value);
|
|
111
|
+
// Check that nested row metadata has new ID
|
|
112
|
+
expect(result['layout.0.subBlocks'].rows?.[0]?.id).toEqual(result['layout.0.subBlocks.0.id'].value);
|
|
113
|
+
});
|
|
114
|
+
it('should preserve non-ID field values when pasting', ()=>{
|
|
115
|
+
const copiedBlockID = new ObjectId().toHexString();
|
|
116
|
+
const formState = {
|
|
117
|
+
layout: {
|
|
118
|
+
valid: true,
|
|
119
|
+
value: 0,
|
|
120
|
+
initialValue: 0,
|
|
121
|
+
rows: []
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const clipboardData = {
|
|
125
|
+
type: 'blocks',
|
|
126
|
+
path: 'layout',
|
|
127
|
+
blocks: [],
|
|
128
|
+
data: {
|
|
129
|
+
'layout.0.id': {
|
|
130
|
+
value: copiedBlockID,
|
|
131
|
+
valid: true
|
|
132
|
+
},
|
|
133
|
+
'layout.0.blockType': {
|
|
134
|
+
value: 'content',
|
|
135
|
+
valid: true
|
|
136
|
+
},
|
|
137
|
+
'layout.0.text': {
|
|
138
|
+
value: 'preserved text content',
|
|
139
|
+
valid: true
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
rowIndex: 0
|
|
143
|
+
};
|
|
144
|
+
const result = mergeFormStateFromClipboard({
|
|
145
|
+
dataFromClipboard: clipboardData,
|
|
146
|
+
formState,
|
|
147
|
+
path: 'layout'
|
|
148
|
+
});
|
|
149
|
+
// Non-ID fields should be preserved
|
|
150
|
+
expect(result['layout.0.blockType'].value).toEqual('content');
|
|
151
|
+
expect(result['layout.0.text'].value).toEqual('preserved text content');
|
|
152
|
+
});
|
|
153
|
+
it('should generate new ID when pasting from row to field', ()=>{
|
|
154
|
+
const copiedBlockID = new ObjectId().toHexString();
|
|
155
|
+
const formState = {
|
|
156
|
+
duplicate: {
|
|
157
|
+
valid: true,
|
|
158
|
+
value: 0,
|
|
159
|
+
initialValue: 0,
|
|
160
|
+
rows: []
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
// Simulating copying from blocks.1 and pasting into duplicate field
|
|
164
|
+
const clipboardData = {
|
|
165
|
+
type: 'blocks',
|
|
166
|
+
path: 'blocks',
|
|
167
|
+
blocks: [],
|
|
168
|
+
data: {
|
|
169
|
+
'blocks.1.id': {
|
|
170
|
+
value: copiedBlockID,
|
|
171
|
+
valid: true
|
|
172
|
+
},
|
|
173
|
+
'blocks.1.blockType': {
|
|
174
|
+
value: 'number',
|
|
175
|
+
valid: true
|
|
176
|
+
},
|
|
177
|
+
'blocks.1.number': {
|
|
178
|
+
value: 342,
|
|
179
|
+
valid: true
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
rowIndex: 1
|
|
183
|
+
};
|
|
184
|
+
const result = mergeFormStateFromClipboard({
|
|
185
|
+
dataFromClipboard: clipboardData,
|
|
186
|
+
formState,
|
|
187
|
+
path: 'duplicate'
|
|
188
|
+
});
|
|
189
|
+
// Check that a new ID was generated
|
|
190
|
+
expect(result['duplicate.0.id']).toBeDefined();
|
|
191
|
+
expect(result['duplicate.0.id'].value).toBeDefined();
|
|
192
|
+
expect(result['duplicate.0.id'].value).not.toEqual(copiedBlockID);
|
|
193
|
+
expect(ObjectId.isValid(result['duplicate.0.id'].value)).toBe(true);
|
|
194
|
+
// Check that the row metadata has the new ID (not the copied ID)
|
|
195
|
+
expect(result.duplicate.rows).toBeDefined();
|
|
196
|
+
expect(result.duplicate.rows).toHaveLength(1);
|
|
197
|
+
expect(result.duplicate.rows[0].id).not.toEqual(copiedBlockID);
|
|
198
|
+
expect(result.duplicate.rows[0].id).toEqual(result['duplicate.0.id'].value);
|
|
199
|
+
// Check that other fields were preserved
|
|
200
|
+
expect(result['duplicate.0.number'].value).toEqual(342);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
describe('array ID regeneration', ()=>{
|
|
204
|
+
it('should generate new IDs when pasting arrays to prevent duplicates', ()=>{
|
|
205
|
+
const copiedArrayID = new ObjectId().toHexString();
|
|
206
|
+
const formState = {
|
|
207
|
+
items: {
|
|
208
|
+
valid: true,
|
|
209
|
+
value: 0,
|
|
210
|
+
initialValue: 0,
|
|
211
|
+
rows: []
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const clipboardData = {
|
|
215
|
+
type: 'array',
|
|
216
|
+
path: 'items',
|
|
217
|
+
fields: [],
|
|
218
|
+
data: {
|
|
219
|
+
'items.0.id': {
|
|
220
|
+
value: copiedArrayID,
|
|
221
|
+
valid: true
|
|
222
|
+
},
|
|
223
|
+
'items.0.text': {
|
|
224
|
+
value: 'test content',
|
|
225
|
+
valid: true
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
rowIndex: 0
|
|
229
|
+
};
|
|
230
|
+
const result = mergeFormStateFromClipboard({
|
|
231
|
+
dataFromClipboard: clipboardData,
|
|
232
|
+
formState,
|
|
233
|
+
path: 'items'
|
|
234
|
+
});
|
|
235
|
+
// Check that a new ID was generated
|
|
236
|
+
expect(result['items.0.id']).toBeDefined();
|
|
237
|
+
expect(result['items.0.id'].value).toBeDefined();
|
|
238
|
+
expect(result['items.0.id'].value).not.toEqual(copiedArrayID);
|
|
239
|
+
expect(ObjectId.isValid(result['items.0.id'].value)).toBe(true);
|
|
240
|
+
// Check that the row metadata also has the new ID
|
|
241
|
+
expect(result.items.rows).toHaveLength(1);
|
|
242
|
+
expect(result.items.rows?.[0]?.id).not.toEqual(copiedArrayID);
|
|
243
|
+
expect(result.items.rows?.[0]?.id).toEqual(result['items.0.id'].value);
|
|
244
|
+
});
|
|
245
|
+
it('should generate new IDs for nested arrays', ()=>{
|
|
246
|
+
const copiedArrayID = new ObjectId().toHexString();
|
|
247
|
+
const copiedNestedArrayID = new ObjectId().toHexString();
|
|
248
|
+
const formState = {
|
|
249
|
+
items: {
|
|
250
|
+
valid: true,
|
|
251
|
+
value: 0,
|
|
252
|
+
initialValue: 0,
|
|
253
|
+
rows: []
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const clipboardData = {
|
|
257
|
+
type: 'array',
|
|
258
|
+
path: 'items',
|
|
259
|
+
fields: [],
|
|
260
|
+
data: {
|
|
261
|
+
'items.0.id': {
|
|
262
|
+
value: copiedArrayID,
|
|
263
|
+
valid: true
|
|
264
|
+
},
|
|
265
|
+
'items.0.text': {
|
|
266
|
+
value: 'parent array',
|
|
267
|
+
valid: true
|
|
268
|
+
},
|
|
269
|
+
'items.0.subArray': {
|
|
270
|
+
value: 1,
|
|
271
|
+
valid: true,
|
|
272
|
+
rows: [
|
|
273
|
+
{
|
|
274
|
+
id: copiedNestedArrayID
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
'items.0.subArray.0.id': {
|
|
279
|
+
value: copiedNestedArrayID,
|
|
280
|
+
valid: true
|
|
281
|
+
},
|
|
282
|
+
'items.0.subArray.0.text': {
|
|
283
|
+
value: 'nested array',
|
|
284
|
+
valid: true
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
rowIndex: 0
|
|
288
|
+
};
|
|
289
|
+
const result = mergeFormStateFromClipboard({
|
|
290
|
+
dataFromClipboard: clipboardData,
|
|
291
|
+
formState,
|
|
292
|
+
path: 'items'
|
|
293
|
+
});
|
|
294
|
+
// Check that parent array got new ID
|
|
295
|
+
expect(result['items.0.id'].value).not.toEqual(copiedArrayID);
|
|
296
|
+
expect(ObjectId.isValid(result['items.0.id'].value)).toBe(true);
|
|
297
|
+
// Check that nested array got new ID
|
|
298
|
+
expect(result['items.0.subArray.0.id'].value).not.toEqual(copiedNestedArrayID);
|
|
299
|
+
expect(ObjectId.isValid(result['items.0.subArray.0.id'].value)).toBe(true);
|
|
300
|
+
// Check that parent and nested IDs are different
|
|
301
|
+
expect(result['items.0.id'].value).not.toEqual(result['items.0.subArray.0.id'].value);
|
|
302
|
+
// Check that parent row metadata has new ID
|
|
303
|
+
expect(result.items.rows?.[0]?.id).toEqual(result['items.0.id'].value);
|
|
304
|
+
// Check that nested row metadata has new ID
|
|
305
|
+
expect(result['items.0.subArray'].rows?.[0]?.id).toEqual(result['items.0.subArray.0.id'].value);
|
|
306
|
+
});
|
|
307
|
+
it('should preserve non-ID field values when pasting arrays', ()=>{
|
|
308
|
+
const copiedArrayID = new ObjectId().toHexString();
|
|
309
|
+
const formState = {
|
|
310
|
+
items: {
|
|
311
|
+
valid: true,
|
|
312
|
+
value: 0,
|
|
313
|
+
initialValue: 0,
|
|
314
|
+
rows: []
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
const clipboardData = {
|
|
318
|
+
type: 'array',
|
|
319
|
+
path: 'items',
|
|
320
|
+
fields: [],
|
|
321
|
+
data: {
|
|
322
|
+
'items.0.id': {
|
|
323
|
+
value: copiedArrayID,
|
|
324
|
+
valid: true
|
|
325
|
+
},
|
|
326
|
+
'items.0.text': {
|
|
327
|
+
value: 'preserved array text',
|
|
328
|
+
valid: true
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
rowIndex: 0
|
|
332
|
+
};
|
|
333
|
+
const result = mergeFormStateFromClipboard({
|
|
334
|
+
dataFromClipboard: clipboardData,
|
|
335
|
+
formState,
|
|
336
|
+
path: 'items'
|
|
337
|
+
});
|
|
338
|
+
// Non-ID fields should be preserved
|
|
339
|
+
expect(result['items.0.text'].value).toEqual('preserved array text');
|
|
340
|
+
});
|
|
341
|
+
it('should generate new ID when pasting from array row to field', ()=>{
|
|
342
|
+
const copiedArrayID = new ObjectId().toHexString();
|
|
343
|
+
const formState = {
|
|
344
|
+
disableSort: {
|
|
345
|
+
valid: true,
|
|
346
|
+
value: 0,
|
|
347
|
+
initialValue: 0,
|
|
348
|
+
rows: []
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
// Simulating copying from items.0 and pasting into disableSort field
|
|
352
|
+
const clipboardData = {
|
|
353
|
+
type: 'array',
|
|
354
|
+
path: 'items',
|
|
355
|
+
fields: [],
|
|
356
|
+
data: {
|
|
357
|
+
'items.0.id': {
|
|
358
|
+
value: copiedArrayID,
|
|
359
|
+
valid: true
|
|
360
|
+
},
|
|
361
|
+
'items.0.text': {
|
|
362
|
+
value: 'row one',
|
|
363
|
+
valid: true
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
rowIndex: 0
|
|
367
|
+
};
|
|
368
|
+
const result = mergeFormStateFromClipboard({
|
|
369
|
+
dataFromClipboard: clipboardData,
|
|
370
|
+
formState,
|
|
371
|
+
path: 'disableSort'
|
|
372
|
+
});
|
|
373
|
+
// Check that a new ID was generated
|
|
374
|
+
expect(result['disableSort.0.id']).toBeDefined();
|
|
375
|
+
expect(result['disableSort.0.id'].value).toBeDefined();
|
|
376
|
+
expect(result['disableSort.0.id'].value).not.toEqual(copiedArrayID);
|
|
377
|
+
expect(ObjectId.isValid(result['disableSort.0.id'].value)).toBe(true);
|
|
378
|
+
// Check that the row metadata has the new ID (not the copied ID)
|
|
379
|
+
expect(result.disableSort.rows).toBeDefined();
|
|
380
|
+
expect(result.disableSort.rows).toHaveLength(1);
|
|
381
|
+
expect(result.disableSort.rows[0].id).not.toEqual(copiedArrayID);
|
|
382
|
+
expect(result.disableSort.rows[0].id).toEqual(result['disableSort.0.id'].value);
|
|
383
|
+
// Check that other fields were preserved
|
|
384
|
+
expect(result['disableSort.0.text'].value).toEqual('row one');
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
//# sourceMappingURL=mergeFormStateFromClipboard.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/elements/ClipboardAction/mergeFormStateFromClipboard.spec.ts"],"sourcesContent":["import type { FormState } from 'payload'\n\nimport ObjectIdImport from 'bson-objectid'\nimport { describe, expect, it } from 'vitest'\n\nimport { mergeFormStateFromClipboard } from './mergeFormStateFromClipboard.js'\nimport type { ClipboardPasteData } from './types.js'\n\nconst ObjectId = (\n 'default' in ObjectIdImport ? ObjectIdImport.default : ObjectIdImport\n) as typeof ObjectIdImport\n\ndescribe('mergeFormStateFromClipboard', () => {\n describe('block ID regeneration', () => {\n it('should generate new IDs when pasting blocks to prevent duplicates', () => {\n const copiedBlockID = new ObjectId().toHexString()\n\n const formState: FormState = {\n layout: {\n valid: true,\n value: 0,\n initialValue: 0,\n rows: [],\n },\n }\n\n const clipboardData: ClipboardPasteData = {\n type: 'blocks',\n path: 'layout',\n blocks: [],\n data: {\n 'layout.0.id': {\n value: copiedBlockID,\n valid: true,\n },\n 'layout.0.blockType': {\n value: 'content',\n valid: true,\n },\n 'layout.0.text': {\n value: 'test content',\n valid: true,\n },\n },\n rowIndex: 0,\n }\n\n const result = mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path: 'layout',\n })\n\n // Check that a new ID was generated\n expect(result['layout.0.id']).toBeDefined()\n expect(result['layout.0.id'].value).toBeDefined()\n expect(result['layout.0.id'].value).not.toEqual(copiedBlockID)\n expect(ObjectId.isValid(result['layout.0.id'].value as string)).toBe(true)\n\n // Check that the row metadata also has the new ID\n expect(result.layout.rows).toHaveLength(1)\n expect(result.layout.rows?.[0]?.id).not.toEqual(copiedBlockID)\n expect(result.layout.rows?.[0]?.id).toEqual(result['layout.0.id'].value)\n })\n\n it('should generate new IDs for nested blocks', () => {\n const copiedBlockID = new ObjectId().toHexString()\n const copiedNestedBlockID = new ObjectId().toHexString()\n\n const formState: FormState = {\n layout: {\n valid: true,\n value: 0,\n initialValue: 0,\n rows: [],\n },\n }\n\n const clipboardData: ClipboardPasteData = {\n type: 'blocks',\n path: 'layout',\n blocks: [],\n data: {\n 'layout.0.id': {\n value: copiedBlockID,\n valid: true,\n },\n 'layout.0.blockType': {\n value: 'container',\n valid: true,\n },\n 'layout.0.subBlocks': {\n value: 1,\n valid: true,\n rows: [{ id: copiedNestedBlockID }],\n },\n 'layout.0.subBlocks.0.id': {\n value: copiedNestedBlockID,\n valid: true,\n },\n 'layout.0.subBlocks.0.blockType': {\n value: 'content',\n valid: true,\n },\n },\n rowIndex: 0,\n }\n\n const result = mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path: 'layout',\n })\n\n // Check that parent block got new ID\n expect(result['layout.0.id'].value).not.toEqual(copiedBlockID)\n expect(ObjectId.isValid(result['layout.0.id'].value as string)).toBe(true)\n\n // Check that nested block got new ID\n expect(result['layout.0.subBlocks.0.id'].value).not.toEqual(copiedNestedBlockID)\n expect(ObjectId.isValid(result['layout.0.subBlocks.0.id'].value as string)).toBe(true)\n\n // Check that parent and nested IDs are different\n expect(result['layout.0.id'].value).not.toEqual(result['layout.0.subBlocks.0.id'].value)\n\n // Check that parent row metadata has new ID\n expect(result.layout.rows?.[0]?.id).toEqual(result['layout.0.id'].value)\n\n // Check that nested row metadata has new ID\n expect(result['layout.0.subBlocks'].rows?.[0]?.id).toEqual(\n result['layout.0.subBlocks.0.id'].value,\n )\n })\n\n it('should preserve non-ID field values when pasting', () => {\n const copiedBlockID = new ObjectId().toHexString()\n\n const formState: FormState = {\n layout: {\n valid: true,\n value: 0,\n initialValue: 0,\n rows: [],\n },\n }\n\n const clipboardData: ClipboardPasteData = {\n type: 'blocks',\n path: 'layout',\n blocks: [],\n data: {\n 'layout.0.id': {\n value: copiedBlockID,\n valid: true,\n },\n 'layout.0.blockType': {\n value: 'content',\n valid: true,\n },\n 'layout.0.text': {\n value: 'preserved text content',\n valid: true,\n },\n },\n rowIndex: 0,\n }\n\n const result = mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path: 'layout',\n })\n\n // Non-ID fields should be preserved\n expect(result['layout.0.blockType'].value).toEqual('content')\n expect(result['layout.0.text'].value).toEqual('preserved text content')\n })\n\n it('should generate new ID when pasting from row to field', () => {\n const copiedBlockID = new ObjectId().toHexString()\n\n const formState: FormState = {\n duplicate: {\n valid: true,\n value: 0,\n initialValue: 0,\n rows: [],\n },\n }\n\n // Simulating copying from blocks.1 and pasting into duplicate field\n const clipboardData = {\n type: 'blocks' as const,\n path: 'blocks',\n blocks: [],\n data: {\n 'blocks.1.id': {\n value: copiedBlockID,\n valid: true,\n },\n 'blocks.1.blockType': {\n value: 'number',\n valid: true,\n },\n 'blocks.1.number': {\n value: 342,\n valid: true,\n },\n },\n rowIndex: 1,\n }\n\n const result = mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path: 'duplicate',\n })\n\n // Check that a new ID was generated\n expect(result['duplicate.0.id']).toBeDefined()\n expect(result['duplicate.0.id'].value).toBeDefined()\n expect(result['duplicate.0.id'].value).not.toEqual(copiedBlockID)\n expect(ObjectId.isValid(result['duplicate.0.id'].value as string)).toBe(true)\n\n // Check that the row metadata has the new ID (not the copied ID)\n expect(result.duplicate.rows).toBeDefined()\n expect(result.duplicate.rows).toHaveLength(1)\n expect(result.duplicate.rows![0].id).not.toEqual(copiedBlockID)\n expect(result.duplicate.rows![0].id).toEqual(result['duplicate.0.id'].value)\n\n // Check that other fields were preserved\n expect(result['duplicate.0.number'].value).toEqual(342)\n })\n })\n\n describe('array ID regeneration', () => {\n it('should generate new IDs when pasting arrays to prevent duplicates', () => {\n const copiedArrayID = new ObjectId().toHexString()\n\n const formState: FormState = {\n items: {\n valid: true,\n value: 0,\n initialValue: 0,\n rows: [],\n },\n }\n\n const clipboardData: ClipboardPasteData = {\n type: 'array',\n path: 'items',\n fields: [],\n data: {\n 'items.0.id': {\n value: copiedArrayID,\n valid: true,\n },\n 'items.0.text': {\n value: 'test content',\n valid: true,\n },\n },\n rowIndex: 0,\n }\n\n const result = mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path: 'items',\n })\n\n // Check that a new ID was generated\n expect(result['items.0.id']).toBeDefined()\n expect(result['items.0.id'].value).toBeDefined()\n expect(result['items.0.id'].value).not.toEqual(copiedArrayID)\n expect(ObjectId.isValid(result['items.0.id'].value as string)).toBe(true)\n\n // Check that the row metadata also has the new ID\n expect(result.items.rows).toHaveLength(1)\n expect(result.items.rows?.[0]?.id).not.toEqual(copiedArrayID)\n expect(result.items.rows?.[0]?.id).toEqual(result['items.0.id'].value)\n })\n\n it('should generate new IDs for nested arrays', () => {\n const copiedArrayID = new ObjectId().toHexString()\n const copiedNestedArrayID = new ObjectId().toHexString()\n\n const formState: FormState = {\n items: {\n valid: true,\n value: 0,\n initialValue: 0,\n rows: [],\n },\n }\n\n const clipboardData: ClipboardPasteData = {\n type: 'array',\n path: 'items',\n fields: [],\n data: {\n 'items.0.id': {\n value: copiedArrayID,\n valid: true,\n },\n 'items.0.text': {\n value: 'parent array',\n valid: true,\n },\n 'items.0.subArray': {\n value: 1,\n valid: true,\n rows: [{ id: copiedNestedArrayID }],\n },\n 'items.0.subArray.0.id': {\n value: copiedNestedArrayID,\n valid: true,\n },\n 'items.0.subArray.0.text': {\n value: 'nested array',\n valid: true,\n },\n },\n rowIndex: 0,\n }\n\n const result = mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path: 'items',\n })\n\n // Check that parent array got new ID\n expect(result['items.0.id'].value).not.toEqual(copiedArrayID)\n expect(ObjectId.isValid(result['items.0.id'].value as string)).toBe(true)\n\n // Check that nested array got new ID\n expect(result['items.0.subArray.0.id'].value).not.toEqual(copiedNestedArrayID)\n expect(ObjectId.isValid(result['items.0.subArray.0.id'].value as string)).toBe(true)\n\n // Check that parent and nested IDs are different\n expect(result['items.0.id'].value).not.toEqual(result['items.0.subArray.0.id'].value)\n\n // Check that parent row metadata has new ID\n expect(result.items.rows?.[0]?.id).toEqual(result['items.0.id'].value)\n\n // Check that nested row metadata has new ID\n expect(result['items.0.subArray'].rows?.[0]?.id).toEqual(\n result['items.0.subArray.0.id'].value,\n )\n })\n\n it('should preserve non-ID field values when pasting arrays', () => {\n const copiedArrayID = new ObjectId().toHexString()\n\n const formState: FormState = {\n items: {\n valid: true,\n value: 0,\n initialValue: 0,\n rows: [],\n },\n }\n\n const clipboardData: ClipboardPasteData = {\n type: 'array',\n path: 'items',\n fields: [],\n data: {\n 'items.0.id': {\n value: copiedArrayID,\n valid: true,\n },\n 'items.0.text': {\n value: 'preserved array text',\n valid: true,\n },\n },\n rowIndex: 0,\n }\n\n const result = mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path: 'items',\n })\n\n // Non-ID fields should be preserved\n expect(result['items.0.text'].value).toEqual('preserved array text')\n })\n\n it('should generate new ID when pasting from array row to field', () => {\n const copiedArrayID = new ObjectId().toHexString()\n\n const formState: FormState = {\n disableSort: {\n valid: true,\n value: 0,\n initialValue: 0,\n rows: [],\n },\n }\n\n // Simulating copying from items.0 and pasting into disableSort field\n const clipboardData = {\n type: 'array' as const,\n path: 'items',\n fields: [],\n data: {\n 'items.0.id': {\n value: copiedArrayID,\n valid: true,\n },\n 'items.0.text': {\n value: 'row one',\n valid: true,\n },\n },\n rowIndex: 0,\n }\n\n const result = mergeFormStateFromClipboard({\n dataFromClipboard: clipboardData,\n formState,\n path: 'disableSort',\n })\n\n // Check that a new ID was generated\n expect(result['disableSort.0.id']).toBeDefined()\n expect(result['disableSort.0.id'].value).toBeDefined()\n expect(result['disableSort.0.id'].value).not.toEqual(copiedArrayID)\n expect(ObjectId.isValid(result['disableSort.0.id'].value as string)).toBe(true)\n\n // Check that the row metadata has the new ID (not the copied ID)\n expect(result.disableSort.rows).toBeDefined()\n expect(result.disableSort.rows).toHaveLength(1)\n expect(result.disableSort.rows![0].id).not.toEqual(copiedArrayID)\n expect(result.disableSort.rows![0].id).toEqual(result['disableSort.0.id'].value)\n\n // Check that other fields were preserved\n expect(result['disableSort.0.text'].value).toEqual('row one')\n })\n })\n})\n"],"names":["ObjectIdImport","describe","expect","it","mergeFormStateFromClipboard","ObjectId","default","copiedBlockID","toHexString","formState","layout","valid","value","initialValue","rows","clipboardData","type","path","blocks","data","rowIndex","result","dataFromClipboard","toBeDefined","not","toEqual","isValid","toBe","toHaveLength","id","copiedNestedBlockID","duplicate","copiedArrayID","items","fields","copiedNestedArrayID","disableSort"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fieldErrors.d.ts","sourceRoot":"","sources":["../../../src/elements/Toasts/fieldErrors.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"fieldErrors.d.ts","sourceRoot":"","sources":["../../../src/elements/Toasts/fieldErrors.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AA6BzB,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG;IACxD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB,CAwBA;AAED,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE;;CAAA,qBAmBhD"}
|
|
@@ -23,8 +23,10 @@ function groupSimilarErrors(items) {
|
|
|
23
23
|
}
|
|
24
24
|
return result;
|
|
25
25
|
}
|
|
26
|
-
function createErrorsFromMessage(message) {
|
|
27
|
-
const
|
|
26
|
+
export function createErrorsFromMessage(message) {
|
|
27
|
+
const colonIndex = message.indexOf(':');
|
|
28
|
+
const intro = colonIndex >= 0 ? message.slice(0, colonIndex) : message;
|
|
29
|
+
const errorsString = colonIndex >= 0 ? message.slice(colonIndex + 1) : undefined;
|
|
28
30
|
if (!errorsString) {
|
|
29
31
|
return {
|
|
30
32
|
message: intro
|
|
@@ -52,7 +54,7 @@ export function FieldErrorsToast({ errorMessage }) {
|
|
|
52
54
|
children: errors[0]
|
|
53
55
|
}, void 0, false, {
|
|
54
56
|
fileName: "src/elements/Toasts/fieldErrors.tsx",
|
|
55
|
-
lineNumber:
|
|
57
|
+
lineNumber: 69,
|
|
56
58
|
columnNumber: 11
|
|
57
59
|
}, this) : /*#__PURE__*/ _jsxDEV("ul", {
|
|
58
60
|
"data-testid": "field-errors",
|
|
@@ -61,19 +63,19 @@ export function FieldErrorsToast({ errorMessage }) {
|
|
|
61
63
|
children: error
|
|
62
64
|
}, index, false, {
|
|
63
65
|
fileName: "src/elements/Toasts/fieldErrors.tsx",
|
|
64
|
-
lineNumber:
|
|
66
|
+
lineNumber: 73,
|
|
65
67
|
columnNumber: 22
|
|
66
68
|
}, this);
|
|
67
69
|
})
|
|
68
70
|
}, void 0, false, {
|
|
69
71
|
fileName: "src/elements/Toasts/fieldErrors.tsx",
|
|
70
|
-
lineNumber:
|
|
72
|
+
lineNumber: 71,
|
|
71
73
|
columnNumber: 11
|
|
72
74
|
}, this) : null
|
|
73
75
|
]
|
|
74
76
|
}, void 0, true, {
|
|
75
77
|
fileName: "src/elements/Toasts/fieldErrors.tsx",
|
|
76
|
-
lineNumber:
|
|
78
|
+
lineNumber: 65,
|
|
77
79
|
columnNumber: 5
|
|
78
80
|
}, this);
|
|
79
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/elements/Toasts/fieldErrors.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\n\nfunction groupSimilarErrors(items: string[]): string[] {\n const result: string[] = []\n\n for (const item of items) {\n if (item) {\n const parts = item.split(' → ')\n let inserted = false\n\n // Find a place where a similar path exists\n for (let i = 0; i < result.length; i++) {\n if (result[i].startsWith(parts[0])) {\n result.splice(i + 1, 0, item)\n inserted = true\n break\n }\n }\n\n // If no similar path was found, add to the end\n if (!inserted) {\n result.push(item)\n }\n }\n }\n\n return result\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../../src/elements/Toasts/fieldErrors.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\n\nfunction groupSimilarErrors(items: string[]): string[] {\n const result: string[] = []\n\n for (const item of items) {\n if (item) {\n const parts = item.split(' → ')\n let inserted = false\n\n // Find a place where a similar path exists\n for (let i = 0; i < result.length; i++) {\n if (result[i].startsWith(parts[0])) {\n result.splice(i + 1, 0, item)\n inserted = true\n break\n }\n }\n\n // If no similar path was found, add to the end\n if (!inserted) {\n result.push(item)\n }\n }\n }\n\n return result\n}\n\nexport function createErrorsFromMessage(message: string): {\n errors?: string[]\n message: string\n} {\n const colonIndex = message.indexOf(':')\n const intro = colonIndex >= 0 ? message.slice(0, colonIndex) : message\n const errorsString = colonIndex >= 0 ? message.slice(colonIndex + 1) : undefined\n\n if (!errorsString) {\n return {\n message: intro,\n }\n }\n\n const errors = errorsString.split(',').map((error) => error.replaceAll(' > ', ' → ').trim())\n\n if (errors.length === 1) {\n return {\n errors,\n message: `${intro}: `,\n }\n }\n\n return {\n errors: groupSimilarErrors(errors),\n message: `${intro} (${errors.length}):`,\n }\n}\n\nexport function FieldErrorsToast({ errorMessage }) {\n const [{ errors, message }] = React.useState(() => createErrorsFromMessage(errorMessage))\n\n return (\n <div>\n {message}\n {Array.isArray(errors) && errors.length > 0 ? (\n errors.length === 1 ? (\n <span data-testid=\"field-error\">{errors[0]}</span>\n ) : (\n <ul data-testid=\"field-errors\">\n {errors.map((error, index) => {\n return <li key={index}>{error}</li>\n })}\n </ul>\n )\n ) : null}\n </div>\n )\n}\n"],"names":["React","groupSimilarErrors","items","result","item","parts","split","inserted","i","length","startsWith","splice","push","createErrorsFromMessage","message","colonIndex","indexOf","intro","slice","errorsString","undefined","errors","map","error","replaceAll","trim","FieldErrorsToast","errorMessage","useState","div","Array","isArray","span","data-testid","ul","index","li"],"mappings":"AAAA;;AAEA,OAAOA,WAAW,QAAO;AAEzB,SAASC,mBAAmBC,KAAe;IACzC,MAAMC,SAAmB,EAAE;IAE3B,KAAK,MAAMC,QAAQF,MAAO;QACxB,IAAIE,MAAM;YACR,MAAMC,QAAQD,KAAKE,KAAK,CAAC;YACzB,IAAIC,WAAW;YAEf,2CAA2C;YAC3C,IAAK,IAAIC,IAAI,GAAGA,IAAIL,OAAOM,MAAM,EAAED,IAAK;gBACtC,IAAIL,MAAM,CAACK,EAAE,CAACE,UAAU,CAACL,KAAK,CAAC,EAAE,GAAG;oBAClCF,OAAOQ,MAAM,CAACH,IAAI,GAAG,GAAGJ;oBACxBG,WAAW;oBACX;gBACF;YACF;YAEA,+CAA+C;YAC/C,IAAI,CAACA,UAAU;gBACbJ,OAAOS,IAAI,CAACR;YACd;QACF;IACF;IAEA,OAAOD;AACT;AAEA,OAAO,SAASU,wBAAwBC,OAAe;IAIrD,MAAMC,aAAaD,QAAQE,OAAO,CAAC;IACnC,MAAMC,QAAQF,cAAc,IAAID,QAAQI,KAAK,CAAC,GAAGH,cAAcD;IAC/D,MAAMK,eAAeJ,cAAc,IAAID,QAAQI,KAAK,CAACH,aAAa,KAAKK;IAEvE,IAAI,CAACD,cAAc;QACjB,OAAO;YACLL,SAASG;QACX;IACF;IAEA,MAAMI,SAASF,aAAab,KAAK,CAAC,KAAKgB,GAAG,CAAC,CAACC,QAAUA,MAAMC,UAAU,CAAC,OAAO,OAAOC,IAAI;IAEzF,IAAIJ,OAAOZ,MAAM,KAAK,GAAG;QACvB,OAAO;YACLY;YACAP,SAAS,GAAGG,MAAM,EAAE,CAAC;QACvB;IACF;IAEA,OAAO;QACLI,QAAQpB,mBAAmBoB;QAC3BP,SAAS,GAAGG,MAAM,EAAE,EAAEI,OAAOZ,MAAM,CAAC,EAAE,CAAC;IACzC;AACF;AAEA,OAAO,SAASiB,iBAAiB,EAAEC,YAAY,EAAE;IAC/C,MAAM,CAAC,EAAEN,MAAM,EAAEP,OAAO,EAAE,CAAC,GAAGd,MAAM4B,QAAQ,CAAC,IAAMf,wBAAwBc;IAE3E,qBACE,QAACE;;YACEf;YACAgB,MAAMC,OAAO,CAACV,WAAWA,OAAOZ,MAAM,GAAG,IACxCY,OAAOZ,MAAM,KAAK,kBAChB,QAACuB;gBAAKC,eAAY;0BAAeZ,MAAM,CAAC,EAAE;;;;;qCAE1C,QAACa;gBAAGD,eAAY;0BACbZ,OAAOC,GAAG,CAAC,CAACC,OAAOY;oBAClB,qBAAO,QAACC;kCAAgBb;uBAARY;;;;;gBAClB;;;;;uBAGF;;;;;;;AAGV"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createErrorsFromMessage } from './fieldErrors.js';
|
|
3
|
+
describe('createErrorsFromMessage', ()=>{
|
|
4
|
+
it('should return the full message when there is no colon', ()=>{
|
|
5
|
+
const result = createErrorsFromMessage('Something went wrong');
|
|
6
|
+
expect(result).toEqual({
|
|
7
|
+
message: 'Something went wrong'
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
it('should split a single field error after the colon', ()=>{
|
|
11
|
+
const result = createErrorsFromMessage('Validation failed: email');
|
|
12
|
+
expect(result).toEqual({
|
|
13
|
+
errors: [
|
|
14
|
+
'email'
|
|
15
|
+
],
|
|
16
|
+
message: 'Validation failed: '
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it('should split multiple comma-separated field errors after the colon', ()=>{
|
|
20
|
+
const result = createErrorsFromMessage('The following fields are invalid: email, name');
|
|
21
|
+
expect(result).toEqual({
|
|
22
|
+
errors: [
|
|
23
|
+
'email',
|
|
24
|
+
'name'
|
|
25
|
+
],
|
|
26
|
+
message: 'The following fields are invalid (2):'
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it('should preserve the full message when it contains multiple colons', ()=>{
|
|
30
|
+
const result = createErrorsFromMessage('With: multiple: colons');
|
|
31
|
+
expect(result).toEqual({
|
|
32
|
+
errors: [
|
|
33
|
+
'multiple: colons'
|
|
34
|
+
],
|
|
35
|
+
message: 'With: '
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it('should replace " > " with " → " in error paths', ()=>{
|
|
39
|
+
const result = createErrorsFromMessage('Invalid: parent > child');
|
|
40
|
+
expect(result).toEqual({
|
|
41
|
+
errors: [
|
|
42
|
+
'parent → child'
|
|
43
|
+
],
|
|
44
|
+
message: 'Invalid: '
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it('should group similar errors and count them', ()=>{
|
|
48
|
+
const result = createErrorsFromMessage('Invalid: blocks > 0, blocks > 1, other');
|
|
49
|
+
expect(result).toEqual({
|
|
50
|
+
errors: [
|
|
51
|
+
'blocks → 0',
|
|
52
|
+
'blocks → 1',
|
|
53
|
+
'other'
|
|
54
|
+
],
|
|
55
|
+
message: 'Invalid (3):'
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=fieldErrors.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/elements/Toasts/fieldErrors.spec.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest'\n\nimport { createErrorsFromMessage } from './fieldErrors.js'\n\ndescribe('createErrorsFromMessage', () => {\n it('should return the full message when there is no colon', () => {\n const result = createErrorsFromMessage('Something went wrong')\n\n expect(result).toEqual({ message: 'Something went wrong' })\n })\n\n it('should split a single field error after the colon', () => {\n const result = createErrorsFromMessage('Validation failed: email')\n\n expect(result).toEqual({ errors: ['email'], message: 'Validation failed: ' })\n })\n\n it('should split multiple comma-separated field errors after the colon', () => {\n const result = createErrorsFromMessage('The following fields are invalid: email, name')\n\n expect(result).toEqual({\n errors: ['email', 'name'],\n message: 'The following fields are invalid (2):',\n })\n })\n\n it('should preserve the full message when it contains multiple colons', () => {\n const result = createErrorsFromMessage('With: multiple: colons')\n\n expect(result).toEqual({ errors: ['multiple: colons'], message: 'With: ' })\n })\n\n it('should replace \" > \" with \" → \" in error paths', () => {\n const result = createErrorsFromMessage('Invalid: parent > child')\n\n expect(result).toEqual({ errors: ['parent → child'], message: 'Invalid: ' })\n })\n\n it('should group similar errors and count them', () => {\n const result = createErrorsFromMessage('Invalid: blocks > 0, blocks > 1, other')\n\n expect(result).toEqual({\n errors: ['blocks → 0', 'blocks → 1', 'other'],\n message: 'Invalid (3):',\n })\n })\n})\n"],"names":["describe","expect","it","createErrorsFromMessage","result","toEqual","message","errors"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,uBAAuB,QAAQ,mBAAkB;AAE1DH,SAAS,2BAA2B;IAClCE,GAAG,yDAAyD;QAC1D,MAAME,SAASD,wBAAwB;QAEvCF,OAAOG,QAAQC,OAAO,CAAC;YAAEC,SAAS;QAAuB;IAC3D;IAEAJ,GAAG,qDAAqD;QACtD,MAAME,SAASD,wBAAwB;QAEvCF,OAAOG,QAAQC,OAAO,CAAC;YAAEE,QAAQ;gBAAC;aAAQ;YAAED,SAAS;QAAsB;IAC7E;IAEAJ,GAAG,sEAAsE;QACvE,MAAME,SAASD,wBAAwB;QAEvCF,OAAOG,QAAQC,OAAO,CAAC;YACrBE,QAAQ;gBAAC;gBAAS;aAAO;YACzBD,SAAS;QACX;IACF;IAEAJ,GAAG,qEAAqE;QACtE,MAAME,SAASD,wBAAwB;QAEvCF,OAAOG,QAAQC,OAAO,CAAC;YAAEE,QAAQ;gBAAC;aAAmB;YAAED,SAAS;QAAS;IAC3E;IAEAJ,GAAG,kDAAkD;QACnD,MAAME,SAASD,wBAAwB;QAEvCF,OAAOG,QAAQC,OAAO,CAAC;YAAEE,QAAQ;gBAAC;aAAiB;YAAED,SAAS;QAAY;IAC5E;IAEAJ,GAAG,8CAA8C;QAC/C,MAAME,SAASD,wBAAwB;QAEvCF,OAAOG,QAAQC,OAAO,CAAC;YACrBE,QAAQ;gBAAC;gBAAc;gBAAc;aAAQ;YAC7CD,SAAS;QACX;IACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/elements/WhereBuilder/Condition/Relationship/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAuD,MAAM,OAAO,CAAA;AAG3E,OAAO,KAAK,EAAE,uBAAuB,IAAI,KAAK,EAAqB,MAAM,YAAY,CAAA;AAQrF,OAAO,cAAc,CAAA;AAOrB,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/elements/WhereBuilder/Condition/Relationship/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAuD,MAAM,OAAO,CAAA;AAG3E,OAAO,KAAK,EAAE,uBAAuB,IAAI,KAAK,EAAqB,MAAM,YAAY,CAAA;AAQrF,OAAO,cAAc,CAAA;AAOrB,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CA2Z9C,CAAA"}
|
|
@@ -14,9 +14,13 @@ import optionsReducer from './optionsReducer.js';
|
|
|
14
14
|
const baseClass = 'condition-value-relationship';
|
|
15
15
|
const maxResultsPerRequest = 10;
|
|
16
16
|
export const RelationshipFilter = (props)=>{
|
|
17
|
-
const { disabled, field: { admin = {}, hasMany, relationTo }, filterOptions, onChange, value } = props;
|
|
17
|
+
const { disabled, field: { admin = {}, hasMany, relationTo }, filterOptions, onChange, operator, value } = props;
|
|
18
18
|
const placeholder = 'placeholder' in admin ? admin?.placeholder : undefined;
|
|
19
19
|
const isSortable = admin?.isSortable;
|
|
20
|
+
const isMultiValue = hasMany || [
|
|
21
|
+
'in',
|
|
22
|
+
'not_in'
|
|
23
|
+
].includes(operator);
|
|
20
24
|
const { config: { routes: { api } }, getEntityConfig } = useConfig();
|
|
21
25
|
const hasMultipleRelations = Array.isArray(relationTo);
|
|
22
26
|
const [options, dispatchOptions] = useReducer(optionsReducer, []);
|
|
@@ -136,7 +140,7 @@ export const RelationshipFilter = (props)=>{
|
|
|
136
140
|
}, []);
|
|
137
141
|
const findOptionsByValue = useCallback(()=>{
|
|
138
142
|
if (value) {
|
|
139
|
-
if (
|
|
143
|
+
if (isMultiValue) {
|
|
140
144
|
if (Array.isArray(value)) {
|
|
141
145
|
return value.map((val)=>{
|
|
142
146
|
if (hasMultipleRelations) {
|
|
@@ -179,7 +183,7 @@ export const RelationshipFilter = (props)=>{
|
|
|
179
183
|
}
|
|
180
184
|
return undefined;
|
|
181
185
|
}, [
|
|
182
|
-
|
|
186
|
+
isMultiValue,
|
|
183
187
|
hasMultipleRelations,
|
|
184
188
|
value,
|
|
185
189
|
options
|
|
@@ -291,7 +295,7 @@ export const RelationshipFilter = (props)=>{
|
|
|
291
295
|
* Load any other options that might exist in the value that were not loaded already
|
|
292
296
|
*/ useEffect(()=>{
|
|
293
297
|
if (value && hasLoadedFirstOptions) {
|
|
294
|
-
if (
|
|
298
|
+
if (isMultiValue) {
|
|
295
299
|
const matchedOptions = findOptionsByValue();
|
|
296
300
|
(matchedOptions || []).forEach((option, i)=>{
|
|
297
301
|
if (!option) {
|
|
@@ -317,7 +321,7 @@ export const RelationshipFilter = (props)=>{
|
|
|
317
321
|
}, [
|
|
318
322
|
addOptionByID,
|
|
319
323
|
findOptionsByValue,
|
|
320
|
-
|
|
324
|
+
isMultiValue,
|
|
321
325
|
hasMultipleRelations,
|
|
322
326
|
relationTo,
|
|
323
327
|
value,
|
|
@@ -336,18 +340,18 @@ export const RelationshipFilter = (props)=>{
|
|
|
336
340
|
children: errorLoading
|
|
337
341
|
}, void 0, false, {
|
|
338
342
|
fileName: "src/elements/WhereBuilder/Condition/Relationship/index.tsx",
|
|
339
|
-
lineNumber:
|
|
343
|
+
lineNumber: 390,
|
|
340
344
|
columnNumber: 9
|
|
341
345
|
}, this) : /*#__PURE__*/ _jsxDEV(ReactSelect, {
|
|
342
346
|
disabled: disabled,
|
|
343
|
-
isMulti:
|
|
347
|
+
isMulti: isMultiValue,
|
|
344
348
|
isSortable: isSortable,
|
|
345
349
|
onChange: (selected)=>{
|
|
346
350
|
if (!selected) {
|
|
347
351
|
onChange(null);
|
|
348
352
|
return;
|
|
349
353
|
}
|
|
350
|
-
if (
|
|
354
|
+
if (isMultiValue && Array.isArray(selected)) {
|
|
351
355
|
onChange(selected ? selected.map((option)=>{
|
|
352
356
|
if (hasMultipleRelations) {
|
|
353
357
|
return {
|
|
@@ -373,12 +377,12 @@ export const RelationshipFilter = (props)=>{
|
|
|
373
377
|
value: valueToRender
|
|
374
378
|
}, void 0, false, {
|
|
375
379
|
fileName: "src/elements/WhereBuilder/Condition/Relationship/index.tsx",
|
|
376
|
-
lineNumber:
|
|
380
|
+
lineNumber: 392,
|
|
377
381
|
columnNumber: 9
|
|
378
382
|
}, this)
|
|
379
383
|
}, void 0, false, {
|
|
380
384
|
fileName: "src/elements/WhereBuilder/Condition/Relationship/index.tsx",
|
|
381
|
-
lineNumber:
|
|
385
|
+
lineNumber: 388,
|
|
382
386
|
columnNumber: 5
|
|
383
387
|
}, this);
|
|
384
388
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/elements/WhereBuilder/Condition/Relationship/index.tsx"],"sourcesContent":["'use client'\nimport type { PaginatedDocs, Where } from 'payload'\n\nimport { formatAdminURL } from 'payload/shared'\nimport * as qs from 'qs-esm'\nimport React, { useCallback, useEffect, useReducer, useState } from 'react'\n\nimport type { Option } from '../../../ReactSelect/types.js'\nimport type { RelationshipFilterProps as Props, ValueWithRelation } from './types.js'\n\nimport { useDebounce } from '../../../../hooks/useDebounce.js'\nimport { useEffectEvent } from '../../../../hooks/useEffectEvent.js'\nimport { useConfig } from '../../../../providers/Config/index.js'\nimport { useLocale } from '../../../../providers/Locale/index.js'\nimport { useTranslation } from '../../../../providers/Translation/index.js'\nimport { ReactSelect } from '../../../ReactSelect/index.js'\nimport './index.scss'\nimport optionsReducer from './optionsReducer.js'\n\nconst baseClass = 'condition-value-relationship'\n\nconst maxResultsPerRequest = 10\n\nexport const RelationshipFilter: React.FC<Props> = (props) => {\n const {\n disabled,\n field: { admin = {}, hasMany, relationTo },\n filterOptions,\n onChange,\n value,\n } = props\n\n const placeholder = 'placeholder' in admin ? admin?.placeholder : undefined\n const isSortable = admin?.isSortable\n\n const {\n config: {\n routes: { api },\n },\n getEntityConfig,\n } = useConfig()\n\n const hasMultipleRelations = Array.isArray(relationTo)\n const [options, dispatchOptions] = useReducer(optionsReducer, [])\n const [search, setSearch] = useState('')\n const debouncedSearch = useDebounce(search, 300)\n const [errorLoading, setErrorLoading] = useState('')\n const [hasLoadedFirstOptions, setHasLoadedFirstOptions] = useState(false)\n const { i18n, t } = useTranslation()\n const locale = useLocale()\n\n const relationSlugs = hasMultipleRelations ? relationTo : [relationTo]\n\n const loadedRelationships = React.useRef<\n Map<\n string,\n {\n hasLoadedAll: boolean\n nextPage: number\n }\n >\n >(\n new Map(\n relationSlugs.map((relation) => [\n relation,\n {\n hasLoadedAll: false,\n nextPage: 1,\n },\n ]),\n ),\n )\n\n const addOptions = useCallback(\n (data, relation) => {\n const collection = getEntityConfig({ collectionSlug: relation })\n dispatchOptions({ type: 'ADD', collection, data, hasMultipleRelations, i18n, relation })\n },\n [hasMultipleRelations, i18n, getEntityConfig],\n )\n\n const loadOptions = useEffectEvent(\n async ({\n abortController,\n relationSlug,\n }: {\n abortController: AbortController\n relationSlug: string\n }) => {\n const loadedRelationship = loadedRelationships.current.get(relationSlug)\n\n if (relationSlug && !loadedRelationship.hasLoadedAll) {\n const collection = getEntityConfig({\n collectionSlug: relationSlug,\n })\n\n const fieldToSearch = collection?.admin?.useAsTitle || 'id'\n\n const where: Where = {\n and: [],\n }\n\n const query = {\n depth: 0,\n limit: maxResultsPerRequest,\n locale: locale.code,\n page: loadedRelationship.nextPage,\n select: {\n [fieldToSearch]: true,\n },\n where,\n }\n\n if (filterOptions && filterOptions?.[relationSlug]) {\n query.where.and.push(filterOptions[relationSlug])\n }\n\n if (debouncedSearch) {\n query.where.and.push({\n [fieldToSearch]: {\n like: debouncedSearch,\n },\n })\n }\n\n try {\n const response = await fetch(\n formatAdminURL({\n apiRoute: api,\n path: `/${relationSlug}${qs.stringify(query, { addQueryPrefix: true })}`,\n }),\n {\n credentials: 'include',\n headers: {\n 'Accept-Language': i18n.language,\n },\n signal: abortController.signal,\n },\n )\n\n if (response.ok) {\n const data: PaginatedDocs = await response.json()\n if (data.docs.length > 0) {\n addOptions(data, relationSlug)\n\n if (data.nextPage) {\n loadedRelationships.current.set(relationSlug, {\n hasLoadedAll: false,\n nextPage: data.nextPage,\n })\n } else {\n loadedRelationships.current.set(relationSlug, {\n hasLoadedAll: true,\n nextPage: null,\n })\n }\n }\n } else {\n setErrorLoading(t('error:unspecific'))\n }\n } catch (e) {\n if (!abortController.signal.aborted) {\n console.error(e) // eslint-disable-line no-console\n }\n }\n }\n\n setHasLoadedFirstOptions(true)\n },\n )\n\n const handleScrollToBottom = React.useCallback(() => {\n const relationshipToLoad = loadedRelationships.current.entries().next().value\n\n if (relationshipToLoad[0] && !relationshipToLoad[1].hasLoadedAll) {\n const abortController = new AbortController()\n\n void loadOptions({\n abortController,\n relationSlug: relationshipToLoad[0],\n })\n }\n }, [])\n\n const findOptionsByValue = useCallback((): Option | Option[] => {\n if (value) {\n if (hasMany) {\n if (Array.isArray(value)) {\n return value.map((val) => {\n if (hasMultipleRelations) {\n let matchedOption: Option\n\n options.forEach((opt) => {\n if (opt.options) {\n opt.options.some((subOpt) => {\n if (subOpt?.value == val.value) {\n matchedOption = subOpt\n return true\n }\n\n return false\n })\n }\n })\n\n return matchedOption\n }\n\n return options.find((opt) => opt.value == val)\n })\n }\n\n return undefined\n }\n\n if (hasMultipleRelations) {\n let matchedOption: Option\n\n const valueWithRelation = value as ValueWithRelation\n\n options.forEach((opt) => {\n if (opt?.options) {\n opt.options.some((subOpt) => {\n if (subOpt?.value == valueWithRelation.value) {\n matchedOption = subOpt\n return true\n }\n return false\n })\n }\n })\n\n return matchedOption\n }\n\n return options.find((opt) => opt.value == value)\n }\n\n return undefined\n }, [hasMany, hasMultipleRelations, value, options])\n\n const handleInputChange = useCallback(\n (input: string) => {\n if (input !== search) {\n dispatchOptions({ type: 'CLEAR', i18n, required: false })\n\n const relationSlugs = Array.isArray(relationTo) ? relationTo : [relationTo]\n\n loadedRelationships.current = new Map(\n relationSlugs.map((relation) => [\n relation,\n {\n hasLoadedAll: false,\n nextPage: 1,\n },\n ]),\n )\n\n setSearch(input)\n }\n },\n [i18n, relationTo, search],\n )\n\n const addOptionByID = useCallback(\n async (id, relation) => {\n if (!errorLoading && id !== 'null' && id && relation) {\n const response = await fetch(\n formatAdminURL({ apiRoute: api, path: `/${relation}/${id}?depth=0` }),\n {\n credentials: 'include',\n headers: {\n 'Accept-Language': i18n.language,\n },\n },\n )\n\n if (response.ok) {\n const data = await response.json()\n addOptions({ docs: [data] }, relation)\n } else {\n // eslint-disable-next-line no-console\n console.error(t('error:loadingDocument', { id }))\n }\n }\n },\n [i18n, addOptions, api, errorLoading, t],\n )\n\n /**\n * When `relationTo` changes externally, reset the options and reload them from scratch\n * The `loadOptions` dependency is a useEffectEvent which has no dependencies of its own\n * This means we can safely depend on it without it triggering this effect to run\n * This is useful because this effect should _only_ run when `relationTo` changes\n */\n useEffect(() => {\n const relations = Array.isArray(relationTo) ? relationTo : [relationTo]\n\n loadedRelationships.current = new Map(\n relations.map((relation) => [\n relation,\n {\n hasLoadedAll: false,\n nextPage: 1,\n },\n ]),\n )\n\n dispatchOptions({ type: 'CLEAR', i18n, required: false })\n setHasLoadedFirstOptions(false)\n\n const abortControllers: AbortController[] = []\n\n relations.forEach((relation) => {\n const abortController = new AbortController()\n\n void loadOptions({\n abortController,\n relationSlug: relation,\n })\n\n abortControllers.push(abortController)\n })\n\n return () => {\n abortControllers.forEach((controller) => {\n if (controller.signal) {\n try {\n controller.abort()\n } catch (_err) {\n // swallow error\n }\n }\n })\n }\n }, [i18n, relationTo, debouncedSearch, filterOptions])\n\n /**\n * Load any other options that might exist in the value that were not loaded already\n */\n useEffect(() => {\n if (value && hasLoadedFirstOptions) {\n if (hasMany) {\n const matchedOptions = findOptionsByValue()\n\n ;((matchedOptions as Option[]) || []).forEach((option, i) => {\n if (!option) {\n if (hasMultipleRelations) {\n void addOptionByID(value[i].value, value[i].relationTo)\n } else {\n void addOptionByID(value[i], relationTo)\n }\n }\n })\n } else {\n const matchedOption = findOptionsByValue()\n\n if (!matchedOption) {\n if (hasMultipleRelations) {\n const valueWithRelation = value as ValueWithRelation\n void addOptionByID(valueWithRelation.value, valueWithRelation.relationTo)\n } else {\n void addOptionByID(value, relationTo)\n }\n }\n }\n }\n }, [\n addOptionByID,\n findOptionsByValue,\n hasMany,\n hasMultipleRelations,\n relationTo,\n value,\n hasLoadedFirstOptions,\n ])\n\n const classes = ['field-type', baseClass, errorLoading && 'error-loading']\n .filter(Boolean)\n .join(' ')\n\n const valueToRender = (findOptionsByValue() || value) as Option\n\n return (\n <div className={classes}>\n {errorLoading ? (\n <div className={`${baseClass}__error-loading`}>{errorLoading}</div>\n ) : (\n <ReactSelect\n disabled={disabled}\n isMulti={hasMany}\n isSortable={isSortable}\n onChange={(selected) => {\n if (!selected) {\n onChange(null)\n return\n }\n\n if (hasMany && Array.isArray(selected)) {\n onChange(\n selected\n ? selected.map((option) => {\n if (hasMultipleRelations) {\n return {\n relationTo: option?.relationTo,\n value: option?.value,\n }\n }\n\n return option?.value\n })\n : null,\n )\n } else if (hasMultipleRelations && !Array.isArray(selected)) {\n onChange({\n relationTo: selected?.relationTo,\n value: selected?.value,\n })\n } else if (!Array.isArray(selected)) {\n onChange(selected?.value)\n }\n }}\n onInputChange={handleInputChange}\n onMenuScrollToBottom={handleScrollToBottom}\n options={options}\n placeholder={placeholder}\n value={valueToRender}\n />\n )}\n </div>\n )\n}\n"],"names":["formatAdminURL","qs","React","useCallback","useEffect","useReducer","useState","useDebounce","useEffectEvent","useConfig","useLocale","useTranslation","ReactSelect","optionsReducer","baseClass","maxResultsPerRequest","RelationshipFilter","props","disabled","field","admin","hasMany","relationTo","filterOptions","onChange","value","placeholder","undefined","isSortable","config","routes","api","getEntityConfig","hasMultipleRelations","Array","isArray","options","dispatchOptions","search","setSearch","debouncedSearch","errorLoading","setErrorLoading","hasLoadedFirstOptions","setHasLoadedFirstOptions","i18n","t","locale","relationSlugs","loadedRelationships","useRef","Map","map","relation","hasLoadedAll","nextPage","addOptions","data","collection","collectionSlug","type","loadOptions","abortController","relationSlug","loadedRelationship","current","get","fieldToSearch","useAsTitle","where","and","query","depth","limit","code","page","select","push","like","response","fetch","apiRoute","path","stringify","addQueryPrefix","credentials","headers","language","signal","ok","json","docs","length","set","e","aborted","console","error","handleScrollToBottom","relationshipToLoad","entries","next","AbortController","findOptionsByValue","val","matchedOption","forEach","opt","some","subOpt","find","valueWithRelation","handleInputChange","input","required","addOptionByID","id","relations","abortControllers","controller","abort","_err","matchedOptions","option","i","classes","filter","Boolean","join","valueToRender","div","className","isMulti","selected","onInputChange","onMenuScrollToBottom"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/elements/WhereBuilder/Condition/Relationship/index.tsx"],"sourcesContent":["'use client'\nimport type { PaginatedDocs, Where } from 'payload'\n\nimport { formatAdminURL } from 'payload/shared'\nimport * as qs from 'qs-esm'\nimport React, { useCallback, useEffect, useReducer, useState } from 'react'\n\nimport type { Option } from '../../../ReactSelect/types.js'\nimport type { RelationshipFilterProps as Props, ValueWithRelation } from './types.js'\n\nimport { useDebounce } from '../../../../hooks/useDebounce.js'\nimport { useEffectEvent } from '../../../../hooks/useEffectEvent.js'\nimport { useConfig } from '../../../../providers/Config/index.js'\nimport { useLocale } from '../../../../providers/Locale/index.js'\nimport { useTranslation } from '../../../../providers/Translation/index.js'\nimport { ReactSelect } from '../../../ReactSelect/index.js'\nimport './index.scss'\nimport optionsReducer from './optionsReducer.js'\n\nconst baseClass = 'condition-value-relationship'\n\nconst maxResultsPerRequest = 10\n\nexport const RelationshipFilter: React.FC<Props> = (props) => {\n const {\n disabled,\n field: { admin = {}, hasMany, relationTo },\n filterOptions,\n onChange,\n operator,\n value,\n } = props\n\n const placeholder = 'placeholder' in admin ? admin?.placeholder : undefined\n const isSortable = admin?.isSortable\n\n const isMultiValue = hasMany || ['in', 'not_in'].includes(operator)\n\n const {\n config: {\n routes: { api },\n },\n getEntityConfig,\n } = useConfig()\n\n const hasMultipleRelations = Array.isArray(relationTo)\n const [options, dispatchOptions] = useReducer(optionsReducer, [])\n const [search, setSearch] = useState('')\n const debouncedSearch = useDebounce(search, 300)\n const [errorLoading, setErrorLoading] = useState('')\n const [hasLoadedFirstOptions, setHasLoadedFirstOptions] = useState(false)\n const { i18n, t } = useTranslation()\n const locale = useLocale()\n\n const relationSlugs = hasMultipleRelations ? relationTo : [relationTo]\n\n const loadedRelationships = React.useRef<\n Map<\n string,\n {\n hasLoadedAll: boolean\n nextPage: number\n }\n >\n >(\n new Map(\n relationSlugs.map((relation) => [\n relation,\n {\n hasLoadedAll: false,\n nextPage: 1,\n },\n ]),\n ),\n )\n\n const addOptions = useCallback(\n (data, relation) => {\n const collection = getEntityConfig({ collectionSlug: relation })\n dispatchOptions({ type: 'ADD', collection, data, hasMultipleRelations, i18n, relation })\n },\n [hasMultipleRelations, i18n, getEntityConfig],\n )\n\n const loadOptions = useEffectEvent(\n async ({\n abortController,\n relationSlug,\n }: {\n abortController: AbortController\n relationSlug: string\n }) => {\n const loadedRelationship = loadedRelationships.current.get(relationSlug)\n\n if (relationSlug && !loadedRelationship.hasLoadedAll) {\n const collection = getEntityConfig({\n collectionSlug: relationSlug,\n })\n\n const fieldToSearch = collection?.admin?.useAsTitle || 'id'\n\n const where: Where = {\n and: [],\n }\n\n const query = {\n depth: 0,\n limit: maxResultsPerRequest,\n locale: locale.code,\n page: loadedRelationship.nextPage,\n select: {\n [fieldToSearch]: true,\n },\n where,\n }\n\n if (filterOptions && filterOptions?.[relationSlug]) {\n query.where.and.push(filterOptions[relationSlug])\n }\n\n if (debouncedSearch) {\n query.where.and.push({\n [fieldToSearch]: {\n like: debouncedSearch,\n },\n })\n }\n\n try {\n const response = await fetch(\n formatAdminURL({\n apiRoute: api,\n path: `/${relationSlug}${qs.stringify(query, { addQueryPrefix: true })}`,\n }),\n {\n credentials: 'include',\n headers: {\n 'Accept-Language': i18n.language,\n },\n signal: abortController.signal,\n },\n )\n\n if (response.ok) {\n const data: PaginatedDocs = await response.json()\n if (data.docs.length > 0) {\n addOptions(data, relationSlug)\n\n if (data.nextPage) {\n loadedRelationships.current.set(relationSlug, {\n hasLoadedAll: false,\n nextPage: data.nextPage,\n })\n } else {\n loadedRelationships.current.set(relationSlug, {\n hasLoadedAll: true,\n nextPage: null,\n })\n }\n }\n } else {\n setErrorLoading(t('error:unspecific'))\n }\n } catch (e) {\n if (!abortController.signal.aborted) {\n console.error(e) // eslint-disable-line no-console\n }\n }\n }\n\n setHasLoadedFirstOptions(true)\n },\n )\n\n const handleScrollToBottom = React.useCallback(() => {\n const relationshipToLoad = loadedRelationships.current.entries().next().value\n\n if (relationshipToLoad[0] && !relationshipToLoad[1].hasLoadedAll) {\n const abortController = new AbortController()\n\n void loadOptions({\n abortController,\n relationSlug: relationshipToLoad[0],\n })\n }\n }, [])\n\n const findOptionsByValue = useCallback((): Option | Option[] => {\n if (value) {\n if (isMultiValue) {\n if (Array.isArray(value)) {\n return value.map((val) => {\n if (hasMultipleRelations) {\n let matchedOption: Option\n\n options.forEach((opt) => {\n if (opt.options) {\n opt.options.some((subOpt) => {\n if (subOpt?.value == val.value) {\n matchedOption = subOpt\n return true\n }\n\n return false\n })\n }\n })\n\n return matchedOption\n }\n\n return options.find((opt) => opt.value == val)\n })\n }\n\n return undefined\n }\n\n if (hasMultipleRelations) {\n let matchedOption: Option\n\n const valueWithRelation = value as ValueWithRelation\n\n options.forEach((opt) => {\n if (opt?.options) {\n opt.options.some((subOpt) => {\n if (subOpt?.value == valueWithRelation.value) {\n matchedOption = subOpt\n return true\n }\n return false\n })\n }\n })\n\n return matchedOption\n }\n\n return options.find((opt) => opt.value == value)\n }\n\n return undefined\n }, [isMultiValue, hasMultipleRelations, value, options])\n\n const handleInputChange = useCallback(\n (input: string) => {\n if (input !== search) {\n dispatchOptions({ type: 'CLEAR', i18n, required: false })\n\n const relationSlugs = Array.isArray(relationTo) ? relationTo : [relationTo]\n\n loadedRelationships.current = new Map(\n relationSlugs.map((relation) => [\n relation,\n {\n hasLoadedAll: false,\n nextPage: 1,\n },\n ]),\n )\n\n setSearch(input)\n }\n },\n [i18n, relationTo, search],\n )\n\n const addOptionByID = useCallback(\n async (id, relation) => {\n if (!errorLoading && id !== 'null' && id && relation) {\n const response = await fetch(\n formatAdminURL({ apiRoute: api, path: `/${relation}/${id}?depth=0` }),\n {\n credentials: 'include',\n headers: {\n 'Accept-Language': i18n.language,\n },\n },\n )\n\n if (response.ok) {\n const data = await response.json()\n addOptions({ docs: [data] }, relation)\n } else {\n // eslint-disable-next-line no-console\n console.error(t('error:loadingDocument', { id }))\n }\n }\n },\n [i18n, addOptions, api, errorLoading, t],\n )\n\n /**\n * When `relationTo` changes externally, reset the options and reload them from scratch\n * The `loadOptions` dependency is a useEffectEvent which has no dependencies of its own\n * This means we can safely depend on it without it triggering this effect to run\n * This is useful because this effect should _only_ run when `relationTo` changes\n */\n useEffect(() => {\n const relations = Array.isArray(relationTo) ? relationTo : [relationTo]\n\n loadedRelationships.current = new Map(\n relations.map((relation) => [\n relation,\n {\n hasLoadedAll: false,\n nextPage: 1,\n },\n ]),\n )\n\n dispatchOptions({ type: 'CLEAR', i18n, required: false })\n setHasLoadedFirstOptions(false)\n\n const abortControllers: AbortController[] = []\n\n relations.forEach((relation) => {\n const abortController = new AbortController()\n\n void loadOptions({\n abortController,\n relationSlug: relation,\n })\n\n abortControllers.push(abortController)\n })\n\n return () => {\n abortControllers.forEach((controller) => {\n if (controller.signal) {\n try {\n controller.abort()\n } catch (_err) {\n // swallow error\n }\n }\n })\n }\n }, [i18n, relationTo, debouncedSearch, filterOptions])\n\n /**\n * Load any other options that might exist in the value that were not loaded already\n */\n useEffect(() => {\n if (value && hasLoadedFirstOptions) {\n if (isMultiValue) {\n const matchedOptions = findOptionsByValue()\n\n ;((matchedOptions as Option[]) || []).forEach((option, i) => {\n if (!option) {\n if (hasMultipleRelations) {\n void addOptionByID(value[i].value, value[i].relationTo)\n } else {\n void addOptionByID(value[i], relationTo)\n }\n }\n })\n } else {\n const matchedOption = findOptionsByValue()\n\n if (!matchedOption) {\n if (hasMultipleRelations) {\n const valueWithRelation = value as ValueWithRelation\n void addOptionByID(valueWithRelation.value, valueWithRelation.relationTo)\n } else {\n void addOptionByID(value, relationTo)\n }\n }\n }\n }\n }, [\n addOptionByID,\n findOptionsByValue,\n isMultiValue,\n hasMultipleRelations,\n relationTo,\n value,\n hasLoadedFirstOptions,\n ])\n\n const classes = ['field-type', baseClass, errorLoading && 'error-loading']\n .filter(Boolean)\n .join(' ')\n\n const valueToRender = (findOptionsByValue() || value) as Option\n\n return (\n <div className={classes}>\n {errorLoading ? (\n <div className={`${baseClass}__error-loading`}>{errorLoading}</div>\n ) : (\n <ReactSelect\n disabled={disabled}\n isMulti={isMultiValue}\n isSortable={isSortable}\n onChange={(selected) => {\n if (!selected) {\n onChange(null)\n return\n }\n\n if (isMultiValue && Array.isArray(selected)) {\n onChange(\n selected\n ? selected.map((option) => {\n if (hasMultipleRelations) {\n return {\n relationTo: option?.relationTo,\n value: option?.value,\n }\n }\n\n return option?.value\n })\n : null,\n )\n } else if (hasMultipleRelations && !Array.isArray(selected)) {\n onChange({\n relationTo: selected?.relationTo,\n value: selected?.value,\n })\n } else if (!Array.isArray(selected)) {\n onChange(selected?.value)\n }\n }}\n onInputChange={handleInputChange}\n onMenuScrollToBottom={handleScrollToBottom}\n options={options}\n placeholder={placeholder}\n value={valueToRender}\n />\n )}\n </div>\n )\n}\n"],"names":["formatAdminURL","qs","React","useCallback","useEffect","useReducer","useState","useDebounce","useEffectEvent","useConfig","useLocale","useTranslation","ReactSelect","optionsReducer","baseClass","maxResultsPerRequest","RelationshipFilter","props","disabled","field","admin","hasMany","relationTo","filterOptions","onChange","operator","value","placeholder","undefined","isSortable","isMultiValue","includes","config","routes","api","getEntityConfig","hasMultipleRelations","Array","isArray","options","dispatchOptions","search","setSearch","debouncedSearch","errorLoading","setErrorLoading","hasLoadedFirstOptions","setHasLoadedFirstOptions","i18n","t","locale","relationSlugs","loadedRelationships","useRef","Map","map","relation","hasLoadedAll","nextPage","addOptions","data","collection","collectionSlug","type","loadOptions","abortController","relationSlug","loadedRelationship","current","get","fieldToSearch","useAsTitle","where","and","query","depth","limit","code","page","select","push","like","response","fetch","apiRoute","path","stringify","addQueryPrefix","credentials","headers","language","signal","ok","json","docs","length","set","e","aborted","console","error","handleScrollToBottom","relationshipToLoad","entries","next","AbortController","findOptionsByValue","val","matchedOption","forEach","opt","some","subOpt","find","valueWithRelation","handleInputChange","input","required","addOptionByID","id","relations","abortControllers","controller","abort","_err","matchedOptions","option","i","classes","filter","Boolean","join","valueToRender","div","className","isMulti","selected","onInputChange","onMenuScrollToBottom"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-types.d.ts","sourceRoot":"","sources":["../../../src/elements/WhereBuilder/field-types.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAa1C,eAAO,MAAM,cAAc;;;GAS1B,CAAA;AA8DD,eAAO,MAAM,mBAAmB,EAAE;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG;QACb,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAC9C,CAAA;CA0DF,CAAA;AAED,eAAO,MAAM,sBAAsB,yBAGhC;IACD,KAAK,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,KAAG;IACF,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE;QACd,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;KACd,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"field-types.d.ts","sourceRoot":"","sources":["../../../src/elements/WhereBuilder/field-types.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAa1C,eAAO,MAAM,cAAc;;;GAS1B,CAAA;AA8DD,eAAO,MAAM,mBAAmB,EAAE;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG;QACb,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAC9C,CAAA;CA0DF,CAAA;AAED,eAAO,MAAM,sBAAsB,yBAGhC;IACD,KAAK,EAAE,WAAW,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,KAAG;IACF,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE;QACd,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;KACd,EAAE,CAAA;CAqBJ,CAAA"}
|
|
@@ -187,16 +187,11 @@ export const fieldTypeConditions = {
|
|
|
187
187
|
export const getValidFieldOperators = ({ field, operator })=>{
|
|
188
188
|
let validOperators = [];
|
|
189
189
|
if (field.type === 'relationship' && Array.isArray(field.relationTo)) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
} else {
|
|
196
|
-
validOperators = [
|
|
197
|
-
...base
|
|
198
|
-
];
|
|
199
|
-
}
|
|
190
|
+
// Polymorphic relationships store {relationTo, value} - in/not_in only match value, not both properties
|
|
191
|
+
validOperators = [
|
|
192
|
+
...equalsOperators,
|
|
193
|
+
exists
|
|
194
|
+
];
|
|
200
195
|
} else {
|
|
201
196
|
validOperators = [
|
|
202
197
|
...fieldTypeConditions[field.type].operators
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/elements/WhereBuilder/field-types.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientField } from 'payload'\n\nconst equalsOperators = [\n {\n label: 'equals',\n value: 'equals',\n },\n {\n label: 'isNotEqualTo',\n value: 'not_equals',\n },\n]\n\nexport const arrayOperators = [\n {\n label: 'isIn',\n value: 'in',\n },\n {\n label: 'isNotIn',\n value: 'not_in',\n },\n]\n\nconst exists = {\n label: 'exists',\n value: 'exists',\n}\n\nconst base = [...equalsOperators, ...arrayOperators, exists]\n\nconst numeric = [\n ...base,\n {\n label: 'isGreaterThan',\n value: 'greater_than',\n },\n {\n label: 'isLessThan',\n value: 'less_than',\n },\n {\n label: 'isLessThanOrEqualTo',\n value: 'less_than_equal',\n },\n {\n label: 'isGreaterThanOrEqualTo',\n value: 'greater_than_equal',\n },\n]\n\nconst geo = [\n ...equalsOperators,\n {\n label: 'near',\n value: 'near',\n },\n]\n\nconst within = {\n label: 'within',\n value: 'within',\n}\n\nconst intersects = {\n label: 'intersects',\n value: 'intersects',\n}\n\nconst like = {\n label: 'isLike',\n value: 'like',\n}\n\nconst notLike = {\n label: 'isNotLike',\n value: 'not_like',\n}\n\nconst contains = {\n label: 'contains',\n value: 'contains',\n}\n\nexport const fieldTypeConditions: {\n [key: string]: {\n component: string\n operators: { label: string; value: string }[]\n }\n} = {\n checkbox: {\n component: 'Text',\n operators: [...equalsOperators, exists],\n },\n code: {\n component: 'Text',\n operators: [...base, like, notLike, contains],\n },\n date: {\n component: 'Date',\n operators: [...numeric, exists],\n },\n email: {\n component: 'Text',\n operators: [...base, contains],\n },\n json: {\n component: 'Text',\n operators: [...base, like, contains, notLike, within, intersects],\n },\n number: {\n component: 'Number',\n operators: [...numeric, exists],\n },\n point: {\n component: 'Point',\n operators: [...geo, exists, within, intersects],\n },\n radio: {\n component: 'Select',\n operators: [...base],\n },\n relationship: {\n component: 'Relationship',\n operators: [...base],\n },\n richText: {\n component: 'Text',\n operators: [...base, like, notLike, contains],\n },\n select: {\n component: 'Select',\n operators: [...base],\n },\n text: {\n component: 'Text',\n operators: [...base, like, notLike, contains],\n },\n textarea: {\n component: 'Text',\n operators: [...base, like, notLike, contains],\n },\n upload: {\n component: 'Relationship',\n operators: [...base],\n },\n}\n\nexport const getValidFieldOperators = ({\n field,\n operator,\n}: {\n field: ClientField\n operator?: string\n}): {\n validOperator: string\n validOperators: {\n label: string\n value: string\n }[]\n} => {\n let validOperators: {\n label: string\n value: string\n }[] = []\n\n if (field.type === 'relationship' && Array.isArray(field.relationTo)) {\n
|
|
1
|
+
{"version":3,"sources":["../../../src/elements/WhereBuilder/field-types.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientField } from 'payload'\n\nconst equalsOperators = [\n {\n label: 'equals',\n value: 'equals',\n },\n {\n label: 'isNotEqualTo',\n value: 'not_equals',\n },\n]\n\nexport const arrayOperators = [\n {\n label: 'isIn',\n value: 'in',\n },\n {\n label: 'isNotIn',\n value: 'not_in',\n },\n]\n\nconst exists = {\n label: 'exists',\n value: 'exists',\n}\n\nconst base = [...equalsOperators, ...arrayOperators, exists]\n\nconst numeric = [\n ...base,\n {\n label: 'isGreaterThan',\n value: 'greater_than',\n },\n {\n label: 'isLessThan',\n value: 'less_than',\n },\n {\n label: 'isLessThanOrEqualTo',\n value: 'less_than_equal',\n },\n {\n label: 'isGreaterThanOrEqualTo',\n value: 'greater_than_equal',\n },\n]\n\nconst geo = [\n ...equalsOperators,\n {\n label: 'near',\n value: 'near',\n },\n]\n\nconst within = {\n label: 'within',\n value: 'within',\n}\n\nconst intersects = {\n label: 'intersects',\n value: 'intersects',\n}\n\nconst like = {\n label: 'isLike',\n value: 'like',\n}\n\nconst notLike = {\n label: 'isNotLike',\n value: 'not_like',\n}\n\nconst contains = {\n label: 'contains',\n value: 'contains',\n}\n\nexport const fieldTypeConditions: {\n [key: string]: {\n component: string\n operators: { label: string; value: string }[]\n }\n} = {\n checkbox: {\n component: 'Text',\n operators: [...equalsOperators, exists],\n },\n code: {\n component: 'Text',\n operators: [...base, like, notLike, contains],\n },\n date: {\n component: 'Date',\n operators: [...numeric, exists],\n },\n email: {\n component: 'Text',\n operators: [...base, contains],\n },\n json: {\n component: 'Text',\n operators: [...base, like, contains, notLike, within, intersects],\n },\n number: {\n component: 'Number',\n operators: [...numeric, exists],\n },\n point: {\n component: 'Point',\n operators: [...geo, exists, within, intersects],\n },\n radio: {\n component: 'Select',\n operators: [...base],\n },\n relationship: {\n component: 'Relationship',\n operators: [...base],\n },\n richText: {\n component: 'Text',\n operators: [...base, like, notLike, contains],\n },\n select: {\n component: 'Select',\n operators: [...base],\n },\n text: {\n component: 'Text',\n operators: [...base, like, notLike, contains],\n },\n textarea: {\n component: 'Text',\n operators: [...base, like, notLike, contains],\n },\n upload: {\n component: 'Relationship',\n operators: [...base],\n },\n}\n\nexport const getValidFieldOperators = ({\n field,\n operator,\n}: {\n field: ClientField\n operator?: string\n}): {\n validOperator: string\n validOperators: {\n label: string\n value: string\n }[]\n} => {\n let validOperators: {\n label: string\n value: string\n }[] = []\n\n if (field.type === 'relationship' && Array.isArray(field.relationTo)) {\n // Polymorphic relationships store {relationTo, value} - in/not_in only match value, not both properties\n validOperators = [...equalsOperators, exists]\n } else {\n validOperators = [...fieldTypeConditions[field.type].operators]\n }\n\n return {\n validOperator:\n operator && validOperators.find(({ value }) => value === operator)\n ? operator\n : validOperators[0].value,\n validOperators,\n }\n}\n"],"names":["equalsOperators","label","value","arrayOperators","exists","base","numeric","geo","within","intersects","like","notLike","contains","fieldTypeConditions","checkbox","component","operators","code","date","email","json","number","point","radio","relationship","richText","select","text","textarea","upload","getValidFieldOperators","field","operator","validOperators","type","Array","isArray","relationTo","validOperator","find"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/ui",
|
|
3
|
-
"version": "3.80.0-internal-debug.
|
|
3
|
+
"version": "3.80.0-internal-debug.cd99b1f",
|
|
4
4
|
"homepage": "https://payloadcms.com",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"ts-essentials": "10.0.3",
|
|
137
137
|
"use-context-selector": "2.0.0",
|
|
138
138
|
"uuid": "10.0.0",
|
|
139
|
-
"@payloadcms/translations": "3.80.0-internal-debug.
|
|
139
|
+
"@payloadcms/translations": "3.80.0-internal-debug.cd99b1f"
|
|
140
140
|
},
|
|
141
141
|
"devDependencies": {
|
|
142
142
|
"@babel/cli": "7.27.2",
|
|
@@ -151,14 +151,14 @@
|
|
|
151
151
|
"babel-plugin-react-compiler": "19.1.0-rc.3",
|
|
152
152
|
"esbuild": "0.27.1",
|
|
153
153
|
"esbuild-sass-plugin": "3.3.1",
|
|
154
|
-
"
|
|
155
|
-
"
|
|
154
|
+
"payload": "3.80.0-internal-debug.cd99b1f",
|
|
155
|
+
"@payloadcms/eslint-config": "3.28.0"
|
|
156
156
|
},
|
|
157
157
|
"peerDependencies": {
|
|
158
158
|
"next": ">=15.2.9 <15.3.0 || >=15.3.9 <15.4.0 || >=15.4.11 <15.5.0 || >=16.2.0-canary.10 <17.0.0",
|
|
159
159
|
"react": "^19.0.1 || ^19.1.2 || ^19.2.1",
|
|
160
160
|
"react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1",
|
|
161
|
-
"payload": "3.80.0-internal-debug.
|
|
161
|
+
"payload": "3.80.0-internal-debug.cd99b1f"
|
|
162
162
|
},
|
|
163
163
|
"engines": {
|
|
164
164
|
"node": "^18.20.2 || >=20.9.0"
|