@payloadcms/ui 4.0.0-canary.19 → 4.0.0-canary.20
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/FieldSelect/reduceFieldOptions.d.ts.map +1 -1
- package/dist/elements/FieldSelect/reduceFieldOptions.js +3 -1
- package/dist/elements/FieldSelect/reduceFieldOptions.js.map +1 -1
- package/dist/elements/FieldSelect/reduceFieldOptions.spec.js +143 -0
- package/dist/elements/FieldSelect/reduceFieldOptions.spec.js.map +1 -0
- package/dist/elements/Table/OrderableRow.d.ts +3 -1
- package/dist/elements/Table/OrderableRow.d.ts.map +1 -1
- package/dist/elements/Table/OrderableRow.js +17 -1
- package/dist/elements/Table/OrderableRow.js.map +1 -1
- package/dist/elements/Table/OrderableTable.d.ts.map +1 -1
- package/dist/elements/Table/OrderableTable.js +19 -2
- package/dist/elements/Table/OrderableTable.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/views/HierarchyList/index.css +1 -1
- package/dist/views/List/index.css +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reduceFieldOptions.d.ts","sourceRoot":"","sources":["../../../src/elements/FieldSelect/reduceFieldOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AAahF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,WAAW,CAAA;IAClB,gBAAgB,EAAE,yBAAyB,CAAA;IAC3C,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,EAAE,aAAa,CAAA;CACrB,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAW,WAAW,KAAG,OAOrD,CAAA;AAEH,eAAO,MAAM,kBAAkB,uEAO5B;IACD,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,CAAA;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAChB;QACE,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,CAAA;KAC/C,GACD,yBAAyB,CAAA;CAC9B,KAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"reduceFieldOptions.d.ts","sourceRoot":"","sources":["../../../src/elements/FieldSelect/reduceFieldOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AAahF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,WAAW,CAAA;IAClB,gBAAgB,EAAE,yBAAyB,CAAA;IAC3C,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,EAAE,aAAa,CAAA;CACrB,CAAA;AAED,eAAO,MAAM,kBAAkB,UAAW,WAAW,KAAG,OAOrD,CAAA;AAEH,eAAO,MAAM,kBAAkB,uEAO5B;IACD,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,CAAA;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAChB;QACE,CAAC,SAAS,EAAE,MAAM,GAAG,yBAAyB,CAAA;KAC/C,GACD,yBAAyB,CAAA;CAC9B,KAAG,WAAW,EA8Fd,CAAA"}
|
|
@@ -47,12 +47,14 @@ export const reduceFieldOptions = ({
|
|
|
47
47
|
return [...fieldsToUse, ...field.tabs.reduce((tabFields, tab) => {
|
|
48
48
|
if ('fields' in tab) {
|
|
49
49
|
const isNamedTab = 'name' in tab && tab.name;
|
|
50
|
+
const namedTabPermissions = isNamedTab && fieldPermissions && fieldPermissions !== true ? fieldPermissions[tab.name] : undefined;
|
|
51
|
+
const tabPermissions = isNamedTab ? namedTabPermissions === true ? true : namedTabPermissions?.fields ?? fieldPermissions : fieldPermissions;
|
|
50
52
|
return [...tabFields, ...reduceFieldOptions({
|
|
51
53
|
fields: tab.fields,
|
|
52
54
|
labelPrefix,
|
|
53
55
|
parentPath: path,
|
|
54
56
|
path: isNamedTab ? createNestedClientFieldPath(path, tab) : path,
|
|
55
|
-
permissions:
|
|
57
|
+
permissions: tabPermissions
|
|
56
58
|
})];
|
|
57
59
|
}
|
|
58
60
|
}, [])];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reduceFieldOptions.js","names":["fieldAffectsData","fieldHasSubFields","fieldIsHiddenOrDisabled","getFieldPermissions","isFieldDisabled","createNestedClientFieldPath","combineFieldLabel","ignoreFromBulkEdit","field","Boolean","type","unique","readOnly","reduceFieldOptions","fields","formState","labelPrefix","parentPath","path","permissions","CustomLabel","customComponents","Label","reduce","fieldsToUse","operation","hasOperationPermission","fieldPermissions","read","hasReadPermission","parentName","includes","split","length","fieldHasLabel","label","prefix","tabs","tabFields","tab","isNamedTab","name","formattedField","value"],"sources":["../../../src/elements/FieldSelect/reduceFieldOptions.ts"],"sourcesContent":["import type { ClientField, FormState, SanitizedFieldPermissions } from 'payload'\n\nimport {\n fieldAffectsData,\n fieldHasSubFields,\n fieldIsHiddenOrDisabled,\n getFieldPermissions,\n isFieldDisabled,\n} from 'payload/shared'\n\nimport { createNestedClientFieldPath } from '../../forms/Form/createNestedClientFieldPath.js'\nimport { combineFieldLabel } from '../../utilities/combineFieldLabel.js'\n\nexport type SelectedField = {\n field: ClientField\n fieldPermissions: SanitizedFieldPermissions\n path: string\n}\n\nexport type FieldOption = {\n label: React.ReactNode\n value: SelectedField\n}\n\nexport const ignoreFromBulkEdit = (field: ClientField): boolean =>\n Boolean(\n (fieldAffectsData(field) || field.type === 'ui') &&\n (isFieldDisabled(field, 'bulkEdit') ||\n field.unique ||\n fieldIsHiddenOrDisabled(field) ||\n ('readOnly' in field && field.readOnly)),\n )\n\nexport const reduceFieldOptions = ({\n fields,\n formState,\n labelPrefix = null,\n parentPath = '',\n path = '',\n permissions,\n}: {\n readonly fields: ClientField[]\n readonly formState?: FormState\n readonly labelPrefix?: React.ReactNode\n readonly parentPath?: string\n readonly path?: string\n readonly permissions:\n | {\n [fieldName: string]: SanitizedFieldPermissions\n }\n | SanitizedFieldPermissions\n}): FieldOption[] => {\n if (!fields) {\n return []\n }\n\n const CustomLabel = formState?.[path]?.customComponents?.Label\n\n return fields?.reduce((fieldsToUse, field) => {\n const {\n operation: hasOperationPermission,\n permissions: fieldPermissions,\n read: hasReadPermission,\n } = getFieldPermissions({\n field,\n operation: 'update',\n parentName: parentPath?.includes('.')\n ? parentPath.split('.')[parentPath.split('.').length - 1]\n : parentPath,\n permissions,\n })\n\n // escape for a variety of reasons, include ui fields as they have `name`.\n if (\n (fieldAffectsData(field) || field.type === 'ui') &&\n (isFieldDisabled(field, 'bulkEdit') ||\n field.unique ||\n fieldIsHiddenOrDisabled(field) ||\n ('readOnly' in field && field.readOnly) ||\n !hasOperationPermission ||\n !hasReadPermission)\n ) {\n return fieldsToUse\n }\n\n if (!(field.type === 'array' || field.type === 'blocks') && fieldHasSubFields(field)) {\n const fieldHasLabel = 'label' in field && field.label\n return [\n ...fieldsToUse,\n ...reduceFieldOptions({\n fields: field.fields,\n labelPrefix: fieldHasLabel\n ? combineFieldLabel({ CustomLabel, field, prefix: labelPrefix })\n : labelPrefix,\n parentPath: path,\n path: createNestedClientFieldPath(path, field),\n permissions: fieldPermissions,\n }),\n ]\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n return [\n ...fieldsToUse,\n ...field.tabs.reduce((tabFields, tab) => {\n if ('fields' in tab) {\n const isNamedTab = 'name' in tab && tab.name\n return [\n ...tabFields,\n ...reduceFieldOptions({\n fields: tab.fields,\n labelPrefix,\n parentPath: path,\n path: isNamedTab ? createNestedClientFieldPath(path, tab as ClientField) : path,\n permissions:
|
|
1
|
+
{"version":3,"file":"reduceFieldOptions.js","names":["fieldAffectsData","fieldHasSubFields","fieldIsHiddenOrDisabled","getFieldPermissions","isFieldDisabled","createNestedClientFieldPath","combineFieldLabel","ignoreFromBulkEdit","field","Boolean","type","unique","readOnly","reduceFieldOptions","fields","formState","labelPrefix","parentPath","path","permissions","CustomLabel","customComponents","Label","reduce","fieldsToUse","operation","hasOperationPermission","fieldPermissions","read","hasReadPermission","parentName","includes","split","length","fieldHasLabel","label","prefix","tabs","tabFields","tab","isNamedTab","name","namedTabPermissions","undefined","tabPermissions","formattedField","value"],"sources":["../../../src/elements/FieldSelect/reduceFieldOptions.ts"],"sourcesContent":["import type { ClientField, FormState, SanitizedFieldPermissions } from 'payload'\n\nimport {\n fieldAffectsData,\n fieldHasSubFields,\n fieldIsHiddenOrDisabled,\n getFieldPermissions,\n isFieldDisabled,\n} from 'payload/shared'\n\nimport { createNestedClientFieldPath } from '../../forms/Form/createNestedClientFieldPath.js'\nimport { combineFieldLabel } from '../../utilities/combineFieldLabel.js'\n\nexport type SelectedField = {\n field: ClientField\n fieldPermissions: SanitizedFieldPermissions\n path: string\n}\n\nexport type FieldOption = {\n label: React.ReactNode\n value: SelectedField\n}\n\nexport const ignoreFromBulkEdit = (field: ClientField): boolean =>\n Boolean(\n (fieldAffectsData(field) || field.type === 'ui') &&\n (isFieldDisabled(field, 'bulkEdit') ||\n field.unique ||\n fieldIsHiddenOrDisabled(field) ||\n ('readOnly' in field && field.readOnly)),\n )\n\nexport const reduceFieldOptions = ({\n fields,\n formState,\n labelPrefix = null,\n parentPath = '',\n path = '',\n permissions,\n}: {\n readonly fields: ClientField[]\n readonly formState?: FormState\n readonly labelPrefix?: React.ReactNode\n readonly parentPath?: string\n readonly path?: string\n readonly permissions:\n | {\n [fieldName: string]: SanitizedFieldPermissions\n }\n | SanitizedFieldPermissions\n}): FieldOption[] => {\n if (!fields) {\n return []\n }\n\n const CustomLabel = formState?.[path]?.customComponents?.Label\n\n return fields?.reduce((fieldsToUse, field) => {\n const {\n operation: hasOperationPermission,\n permissions: fieldPermissions,\n read: hasReadPermission,\n } = getFieldPermissions({\n field,\n operation: 'update',\n parentName: parentPath?.includes('.')\n ? parentPath.split('.')[parentPath.split('.').length - 1]\n : parentPath,\n permissions,\n })\n\n // escape for a variety of reasons, include ui fields as they have `name`.\n if (\n (fieldAffectsData(field) || field.type === 'ui') &&\n (isFieldDisabled(field, 'bulkEdit') ||\n field.unique ||\n fieldIsHiddenOrDisabled(field) ||\n ('readOnly' in field && field.readOnly) ||\n !hasOperationPermission ||\n !hasReadPermission)\n ) {\n return fieldsToUse\n }\n\n if (!(field.type === 'array' || field.type === 'blocks') && fieldHasSubFields(field)) {\n const fieldHasLabel = 'label' in field && field.label\n return [\n ...fieldsToUse,\n ...reduceFieldOptions({\n fields: field.fields,\n labelPrefix: fieldHasLabel\n ? combineFieldLabel({ CustomLabel, field, prefix: labelPrefix })\n : labelPrefix,\n parentPath: path,\n path: createNestedClientFieldPath(path, field),\n permissions: fieldPermissions,\n }),\n ]\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n return [\n ...fieldsToUse,\n ...field.tabs.reduce((tabFields, tab) => {\n if ('fields' in tab) {\n const isNamedTab = 'name' in tab && tab.name\n\n const namedTabPermissions =\n isNamedTab && fieldPermissions && fieldPermissions !== true\n ? fieldPermissions[tab.name]\n : undefined\n\n const tabPermissions = isNamedTab\n ? namedTabPermissions === true\n ? true\n : (namedTabPermissions?.fields ?? fieldPermissions)\n : fieldPermissions\n\n return [\n ...tabFields,\n ...reduceFieldOptions({\n fields: tab.fields,\n labelPrefix,\n parentPath: path,\n path: isNamedTab ? createNestedClientFieldPath(path, tab as ClientField) : path,\n permissions: tabPermissions,\n }),\n ]\n }\n }, []),\n ]\n }\n\n const formattedField: FieldOption = {\n label: combineFieldLabel({ CustomLabel, field, prefix: labelPrefix }),\n value: {\n field,\n fieldPermissions: fieldPermissions as SanitizedFieldPermissions,\n path: createNestedClientFieldPath(path, field),\n },\n }\n\n return [...fieldsToUse, formattedField]\n }, [])\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
// Mock the JSX label builder: irrelevant to permission logic and avoids needing a React runtime here.
|
|
3
|
+
vi.mock('../../utilities/combineFieldLabel.js', () => ({
|
|
4
|
+
combineFieldLabel: () => null
|
|
5
|
+
}));
|
|
6
|
+
import { reduceFieldOptions } from './reduceFieldOptions.js';
|
|
7
|
+
const text = name => ({
|
|
8
|
+
name,
|
|
9
|
+
type: 'text'
|
|
10
|
+
});
|
|
11
|
+
const namesOf = options => options.map(option => 'name' in option.value.field ? option.value.field.name : undefined);
|
|
12
|
+
const optionsFor = (fields, permissions) => reduceFieldOptions({
|
|
13
|
+
fields,
|
|
14
|
+
permissions: permissions
|
|
15
|
+
});
|
|
16
|
+
// Granted sets the operation to `true`; denied omits the key, mirroring `populateFieldPermissions`.
|
|
17
|
+
const granted = {
|
|
18
|
+
create: true,
|
|
19
|
+
read: true,
|
|
20
|
+
update: true
|
|
21
|
+
};
|
|
22
|
+
const readOnly = {
|
|
23
|
+
create: true,
|
|
24
|
+
read: true
|
|
25
|
+
};
|
|
26
|
+
describe('reduceFieldOptions', () => {
|
|
27
|
+
it('excludes a restricted field inside a named tab while keeping its clean siblings', () => {
|
|
28
|
+
const fields = [{
|
|
29
|
+
type: 'tabs',
|
|
30
|
+
tabs: [{
|
|
31
|
+
name: 'namedTab',
|
|
32
|
+
fields: [text('okInTab'), text('restrictedInTab')]
|
|
33
|
+
}]
|
|
34
|
+
}];
|
|
35
|
+
const permissions = {
|
|
36
|
+
namedTab: {
|
|
37
|
+
...granted,
|
|
38
|
+
fields: {
|
|
39
|
+
okInTab: granted,
|
|
40
|
+
restrictedInTab: readOnly
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const options = optionsFor(fields, permissions);
|
|
45
|
+
const names = namesOf(options);
|
|
46
|
+
expect(names).toContain('okInTab');
|
|
47
|
+
expect(names).not.toContain('restrictedInTab');
|
|
48
|
+
});
|
|
49
|
+
it('excludes a restricted field inside a nested named tab', () => {
|
|
50
|
+
const fields = [{
|
|
51
|
+
type: 'tabs',
|
|
52
|
+
tabs: [{
|
|
53
|
+
name: 'outerTab',
|
|
54
|
+
fields: [{
|
|
55
|
+
type: 'tabs',
|
|
56
|
+
tabs: [{
|
|
57
|
+
name: 'innerTab',
|
|
58
|
+
fields: [text('deepOk'), text('deepRestricted')]
|
|
59
|
+
}]
|
|
60
|
+
}]
|
|
61
|
+
}]
|
|
62
|
+
}];
|
|
63
|
+
const permissions = {
|
|
64
|
+
outerTab: {
|
|
65
|
+
...granted,
|
|
66
|
+
fields: {
|
|
67
|
+
innerTab: {
|
|
68
|
+
...granted,
|
|
69
|
+
fields: {
|
|
70
|
+
deepOk: granted,
|
|
71
|
+
deepRestricted: readOnly
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const options = optionsFor(fields, permissions);
|
|
78
|
+
const names = namesOf(options);
|
|
79
|
+
expect(names).toContain('deepOk');
|
|
80
|
+
expect(names).not.toContain('deepRestricted');
|
|
81
|
+
});
|
|
82
|
+
it('only affects the named tab that contains the restricted field', () => {
|
|
83
|
+
const fields = [{
|
|
84
|
+
type: 'tabs',
|
|
85
|
+
tabs: [{
|
|
86
|
+
name: 'cleanTab',
|
|
87
|
+
fields: [text('cleanField')]
|
|
88
|
+
}, {
|
|
89
|
+
name: 'restrictedHostTab',
|
|
90
|
+
fields: [text('restrictedField')]
|
|
91
|
+
}]
|
|
92
|
+
}];
|
|
93
|
+
const permissions = {
|
|
94
|
+
cleanTab: {
|
|
95
|
+
...granted,
|
|
96
|
+
fields: {
|
|
97
|
+
cleanField: granted
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
restrictedHostTab: {
|
|
101
|
+
...granted,
|
|
102
|
+
fields: {
|
|
103
|
+
restrictedField: readOnly
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const options = optionsFor(fields, permissions);
|
|
108
|
+
const names = namesOf(options);
|
|
109
|
+
expect(names).toContain('cleanField');
|
|
110
|
+
expect(names).not.toContain('restrictedField');
|
|
111
|
+
});
|
|
112
|
+
it('includes every named-tab field when permissions are fully granted', () => {
|
|
113
|
+
const fields = [{
|
|
114
|
+
type: 'tabs',
|
|
115
|
+
tabs: [{
|
|
116
|
+
name: 'namedTab',
|
|
117
|
+
fields: [text('a'), text('b')]
|
|
118
|
+
}]
|
|
119
|
+
}];
|
|
120
|
+
const options = optionsFor(fields, true);
|
|
121
|
+
const names = namesOf(options);
|
|
122
|
+
expect(names).toEqual(expect.arrayContaining(['a', 'b']));
|
|
123
|
+
});
|
|
124
|
+
it('applies parent permissions to an unnamed tab and excludes its restricted fields', () => {
|
|
125
|
+
const fields = [{
|
|
126
|
+
type: 'tabs',
|
|
127
|
+
tabs: [{
|
|
128
|
+
label: 'Unnamed',
|
|
129
|
+
fields: [text('okAtRoot'), text('restrictedAtRoot')]
|
|
130
|
+
}]
|
|
131
|
+
}];
|
|
132
|
+
// Unnamed tabs share the parent's permission map, so subfields resolve at the parent level.
|
|
133
|
+
const permissions = {
|
|
134
|
+
okAtRoot: granted,
|
|
135
|
+
restrictedAtRoot: readOnly
|
|
136
|
+
};
|
|
137
|
+
const options = optionsFor(fields, permissions);
|
|
138
|
+
const names = namesOf(options);
|
|
139
|
+
expect(names).toContain('okAtRoot');
|
|
140
|
+
expect(names).not.toContain('restrictedAtRoot');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
//# sourceMappingURL=reduceFieldOptions.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reduceFieldOptions.spec.js","names":["describe","expect","it","vi","mock","combineFieldLabel","reduceFieldOptions","text","name","type","namesOf","options","map","option","value","field","undefined","optionsFor","fields","permissions","granted","create","read","update","readOnly","tabs","namedTab","okInTab","restrictedInTab","names","toContain","not","outerTab","innerTab","deepOk","deepRestricted","cleanTab","cleanField","restrictedHostTab","restrictedField","toEqual","arrayContaining","label","okAtRoot","restrictedAtRoot"],"sources":["../../../src/elements/FieldSelect/reduceFieldOptions.spec.ts"],"sourcesContent":["import type { ClientField } from 'payload'\n\nimport { describe, expect, it, vi } from 'vitest'\n\nimport type { FieldOption } from './reduceFieldOptions.js'\n\n// Mock the JSX label builder: irrelevant to permission logic and avoids needing a React runtime here.\nvi.mock('../../utilities/combineFieldLabel.js', () => ({\n combineFieldLabel: () => null,\n}))\n\nimport { reduceFieldOptions } from './reduceFieldOptions.js'\n\nconst text = (name: string): ClientField => ({ name, type: 'text' }) as ClientField\n\nconst namesOf = (options: FieldOption[]): (string | undefined)[] =>\n options.map((option) => ('name' in option.value.field ? option.value.field.name : undefined))\n\n// A denied field omits the operation key at runtime, which `SanitizedFieldPermissions` can't express — cast at the boundary.\ntype Permissions = Parameters<typeof reduceFieldOptions>[0]['permissions']\nconst optionsFor = (fields: ClientField[], permissions: unknown): FieldOption[] =>\n reduceFieldOptions({ fields, permissions: permissions as Permissions })\n\n// Granted sets the operation to `true`; denied omits the key, mirroring `populateFieldPermissions`.\nconst granted = { create: true, read: true, update: true }\nconst readOnly = { create: true, read: true }\n\ndescribe('reduceFieldOptions', () => {\n it('excludes a restricted field inside a named tab while keeping its clean siblings', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [{ name: 'namedTab', fields: [text('okInTab'), text('restrictedInTab')] }],\n } as ClientField,\n ]\n\n const permissions = {\n namedTab: {\n ...granted,\n fields: {\n okInTab: granted,\n restrictedInTab: readOnly,\n },\n },\n }\n\n const options = optionsFor(fields, permissions)\n const names = namesOf(options)\n\n expect(names).toContain('okInTab')\n expect(names).not.toContain('restrictedInTab')\n })\n\n it('excludes a restricted field inside a nested named tab', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [\n {\n name: 'outerTab',\n fields: [\n {\n type: 'tabs',\n tabs: [{ name: 'innerTab', fields: [text('deepOk'), text('deepRestricted')] }],\n },\n ],\n },\n ],\n } as ClientField,\n ]\n\n const permissions = {\n outerTab: {\n ...granted,\n fields: {\n innerTab: {\n ...granted,\n fields: {\n deepOk: granted,\n deepRestricted: readOnly,\n },\n },\n },\n },\n }\n\n const options = optionsFor(fields, permissions)\n const names = namesOf(options)\n\n expect(names).toContain('deepOk')\n expect(names).not.toContain('deepRestricted')\n })\n\n it('only affects the named tab that contains the restricted field', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [\n { name: 'cleanTab', fields: [text('cleanField')] },\n { name: 'restrictedHostTab', fields: [text('restrictedField')] },\n ],\n } as ClientField,\n ]\n\n const permissions = {\n cleanTab: { ...granted, fields: { cleanField: granted } },\n restrictedHostTab: { ...granted, fields: { restrictedField: readOnly } },\n }\n\n const options = optionsFor(fields, permissions)\n const names = namesOf(options)\n\n expect(names).toContain('cleanField')\n expect(names).not.toContain('restrictedField')\n })\n\n it('includes every named-tab field when permissions are fully granted', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [{ name: 'namedTab', fields: [text('a'), text('b')] }],\n } as ClientField,\n ]\n\n const options = optionsFor(fields, true)\n const names = namesOf(options)\n\n expect(names).toEqual(expect.arrayContaining(['a', 'b']))\n })\n\n it('applies parent permissions to an unnamed tab and excludes its restricted fields', () => {\n const fields: ClientField[] = [\n {\n type: 'tabs',\n tabs: [{ label: 'Unnamed', fields: [text('okAtRoot'), text('restrictedAtRoot')] }],\n } as ClientField,\n ]\n\n // Unnamed tabs share the parent's permission map, so subfields resolve at the parent level.\n const permissions = {\n okAtRoot: granted,\n restrictedAtRoot: readOnly,\n }\n\n const options = optionsFor(fields, permissions)\n const names = namesOf(options)\n\n expect(names).toContain('okAtRoot')\n expect(names).not.toContain('restrictedAtRoot')\n })\n})\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,8 +6,10 @@ export type Props = {
|
|
|
6
6
|
readonly columns: Column[];
|
|
7
7
|
readonly dragAttributes?: HTMLAttributes<unknown>;
|
|
8
8
|
readonly dragListeners?: DraggableSyntheticListeners;
|
|
9
|
+
readonly isOrderable?: boolean;
|
|
10
|
+
readonly onDisabledDragAttempt?: () => void;
|
|
9
11
|
readonly ref?: Ref<HTMLTableRowElement>;
|
|
10
12
|
readonly rowId: number | string;
|
|
11
13
|
} & HTMLAttributes<HTMLTableRowElement>;
|
|
12
|
-
export declare const OrderableRow: ({ cellMap, columns, dragAttributes, dragListeners, rowId, ...rest }: Props) => import("react").JSX.Element;
|
|
14
|
+
export declare const OrderableRow: ({ cellMap, columns, dragAttributes, dragListeners, isOrderable, onDisabledDragAttempt, rowId, ...rest }: Props) => import("react").JSX.Element;
|
|
13
15
|
//# sourceMappingURL=OrderableRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderableRow.d.ts","sourceRoot":"","sources":["../../../src/elements/Table/OrderableRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"OrderableRow.d.ts","sourceRoot":"","sources":["../../../src/elements/Table/OrderableRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAiB,GAAG,EAAE,MAAM,OAAO,CAAA;AAE/D,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IACjD,QAAQ,CAAC,aAAa,CAAC,EAAE,2BAA2B,CAAA;IACpD,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3C,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAA;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAChC,GAAG,cAAc,CAAC,mBAAmB,CAAC,CAAA;AAEvC,eAAO,MAAM,YAAY,4GAStB,KAAK,gCA+CP,CAAA"}
|
|
@@ -4,6 +4,8 @@ export const OrderableRow = ({
|
|
|
4
4
|
columns,
|
|
5
5
|
dragAttributes = {},
|
|
6
6
|
dragListeners = {},
|
|
7
|
+
isOrderable = true,
|
|
8
|
+
onDisabledDragAttempt,
|
|
7
9
|
rowId,
|
|
8
10
|
...rest
|
|
9
11
|
}) => /*#__PURE__*/_jsx("tr", {
|
|
@@ -16,11 +18,25 @@ export const OrderableRow = ({
|
|
|
16
18
|
const cell = col.renderedCells[cellMap[rowId]];
|
|
17
19
|
// For drag handles, wrap in div with drag attributes
|
|
18
20
|
if (accessor === '_dragHandle') {
|
|
21
|
+
// When not orderable, dnd-kit's own drag listeners are disabled, so pressing the keys
|
|
22
|
+
// it would normally use to start a drag does nothing silently. Intercept them here to
|
|
23
|
+
// tell the user why instead.
|
|
24
|
+
const dragHandleListeners = {
|
|
25
|
+
...dragListeners,
|
|
26
|
+
onKeyDown: event => {
|
|
27
|
+
if (!isOrderable && (event.code === 'Space' || event.code === 'Enter')) {
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
onDisabledDragAttempt?.();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
dragListeners?.onKeyDown?.(event);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
19
35
|
return /*#__PURE__*/_jsx("td", {
|
|
20
36
|
className: `cell-${accessor}`,
|
|
21
37
|
children: /*#__PURE__*/_jsx("div", {
|
|
22
38
|
...dragAttributes,
|
|
23
|
-
...
|
|
39
|
+
...dragHandleListeners,
|
|
24
40
|
children: cell
|
|
25
41
|
})
|
|
26
42
|
}, colIndex);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderableRow.js","names":["OrderableRow","cellMap","columns","dragAttributes","dragListeners","rowId","rest","_jsx","map","col","colIndex","accessor","cell","renderedCells","className","isLinkedColumn","filter","Boolean","join"],"sources":["../../../src/elements/Table/OrderableRow.tsx"],"sourcesContent":["import type { DraggableSyntheticListeners } from '@dnd-kit/core'\nimport type { Column } from 'payload'\nimport type { HTMLAttributes, Ref } from 'react'\n\nexport type Props = {\n readonly cellMap: Record<string, number>\n readonly columns: Column[]\n readonly dragAttributes?: HTMLAttributes<unknown>\n readonly dragListeners?: DraggableSyntheticListeners\n readonly ref?: Ref<HTMLTableRowElement>\n readonly rowId: number | string\n} & HTMLAttributes<HTMLTableRowElement>\n\nexport const OrderableRow = ({\n cellMap,\n columns,\n dragAttributes = {},\n dragListeners = {},\n rowId,\n ...rest\n}: Props) => (\n <tr {...rest}>\n {columns.map((col, colIndex) => {\n const { accessor } = col\n\n // Use the cellMap to find which index in the renderedCells to use\n const cell = col.renderedCells[cellMap[rowId]]\n\n // For drag handles, wrap in div with drag attributes\n if (accessor === '_dragHandle') {\n return (\n <td className={`cell-${accessor}`} key={colIndex}>\n <div {...dragAttributes} {...
|
|
1
|
+
{"version":3,"file":"OrderableRow.js","names":["OrderableRow","cellMap","columns","dragAttributes","dragListeners","isOrderable","onDisabledDragAttempt","rowId","rest","_jsx","map","col","colIndex","accessor","cell","renderedCells","dragHandleListeners","onKeyDown","event","code","preventDefault","className","isLinkedColumn","filter","Boolean","join"],"sources":["../../../src/elements/Table/OrderableRow.tsx"],"sourcesContent":["import type { DraggableSyntheticListeners } from '@dnd-kit/core'\nimport type { Column } from 'payload'\nimport type { HTMLAttributes, KeyboardEvent, Ref } from 'react'\n\nexport type Props = {\n readonly cellMap: Record<string, number>\n readonly columns: Column[]\n readonly dragAttributes?: HTMLAttributes<unknown>\n readonly dragListeners?: DraggableSyntheticListeners\n readonly isOrderable?: boolean\n readonly onDisabledDragAttempt?: () => void\n readonly ref?: Ref<HTMLTableRowElement>\n readonly rowId: number | string\n} & HTMLAttributes<HTMLTableRowElement>\n\nexport const OrderableRow = ({\n cellMap,\n columns,\n dragAttributes = {},\n dragListeners = {},\n isOrderable = true,\n onDisabledDragAttempt,\n rowId,\n ...rest\n}: Props) => (\n <tr {...rest}>\n {columns.map((col, colIndex) => {\n const { accessor } = col\n\n // Use the cellMap to find which index in the renderedCells to use\n const cell = col.renderedCells[cellMap[rowId]]\n\n // For drag handles, wrap in div with drag attributes\n if (accessor === '_dragHandle') {\n // When not orderable, dnd-kit's own drag listeners are disabled, so pressing the keys\n // it would normally use to start a drag does nothing silently. Intercept them here to\n // tell the user why instead.\n const dragHandleListeners = {\n ...dragListeners,\n onKeyDown: (event: KeyboardEvent) => {\n if (!isOrderable && (event.code === 'Space' || event.code === 'Enter')) {\n event.preventDefault()\n onDisabledDragAttempt?.()\n return\n }\n\n dragListeners?.onKeyDown?.(event)\n },\n }\n\n return (\n <td className={`cell-${accessor}`} key={colIndex}>\n <div {...dragAttributes} {...dragHandleListeners}>\n {cell}\n </div>\n </td>\n )\n }\n\n return (\n <td\n className={[`cell-${accessor}`, col.isLinkedColumn && 'cell--linked']\n .filter(Boolean)\n .join(' ')}\n key={colIndex}\n >\n {cell}\n </td>\n )\n })}\n </tr>\n)\n"],"mappings":";AAeA,OAAO,MAAMA,YAAA,GAAeA,CAAC;EAC3BC,OAAO;EACPC,OAAO;EACPC,cAAA,GAAiB,CAAC,CAAC;EACnBC,aAAA,GAAgB,CAAC,CAAC;EAClBC,WAAA,GAAc,IAAI;EAClBC,qBAAqB;EACrBC,KAAK;EACL,GAAGC;AAAA,CACG,kBACNC,IAAA,CAAC;EAAI,GAAGD,IAAI;YACTN,OAAA,CAAQQ,GAAG,CAAC,CAACC,GAAA,EAAKC,QAAA;IACjB,MAAM;MAAEC;IAAQ,CAAE,GAAGF,GAAA;IAErB;IACA,MAAMG,IAAA,GAAOH,GAAA,CAAII,aAAa,CAACd,OAAO,CAACM,KAAA,CAAM,CAAC;IAE9C;IACA,IAAIM,QAAA,KAAa,eAAe;MAC9B;MACA;MACA;MACA,MAAMG,mBAAA,GAAsB;QAC1B,GAAGZ,aAAa;QAChBa,SAAA,EAAYC,KAAA;UACV,IAAI,CAACb,WAAA,KAAgBa,KAAA,CAAMC,IAAI,KAAK,WAAWD,KAAA,CAAMC,IAAI,KAAK,OAAM,GAAI;YACtED,KAAA,CAAME,cAAc;YACpBd,qBAAA;YACA;UACF;UAEAF,aAAA,EAAea,SAAA,GAAYC,KAAA;QAC7B;MACF;MAEA,oBACET,IAAA,CAAC;QAAGY,SAAA,EAAW,QAAQR,QAAA,EAAU;kBAC/B,aAAAJ,IAAA,CAAC;UAAK,GAAGN,cAAc;UAAG,GAAGa,mBAAmB;oBAC7CF;;SAFmCF,QAAA;IAM5C;IAEA,oBACEH,IAAA,CAAC;MACCY,SAAA,EAAW,CAAC,QAAQR,QAAA,EAAU,EAAEF,GAAA,CAAIW,cAAc,IAAI,eAAe,CAClEC,MAAM,CAACC,OAAA,EACPC,IAAI,CAAC;gBAGPX;OAFIF,QAAA;EAKX","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderableTable.d.ts","sourceRoot":"","sources":["../../../src/elements/Table/OrderableTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAyB,MAAM,SAAS,CAAA;AAEpF,OAAO,aAAa,CAAA;AAIpB,OAAO,KAA8B,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"OrderableTable.d.ts","sourceRoot":"","sources":["../../../src/elements/Table/OrderableTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAyB,MAAM,SAAS,CAAA;AAEpF,OAAO,aAAa,CAAA;AAIpB,OAAO,KAA8B,MAAM,OAAO,CAAA;AAiBlD,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtC,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAA;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACnC,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAgN1C,CAAA"}
|
|
@@ -9,11 +9,14 @@ import { toast } from 'sonner';
|
|
|
9
9
|
import { useConfig } from '../../providers/Config/index.js';
|
|
10
10
|
import { useListQuery } from '../../providers/ListQuery/index.js';
|
|
11
11
|
import { useLocale } from '../../providers/Locale/index.js';
|
|
12
|
+
import { useTranslation } from '../../providers/Translation/index.js';
|
|
12
13
|
import { DraggableSortableItem } from '../DraggableSortable/DraggableSortableItem/index.js';
|
|
13
14
|
import { DraggableSortable } from '../DraggableSortable/index.js';
|
|
14
15
|
import { OrderableRow } from './OrderableRow.js';
|
|
15
16
|
import { OrderableRowDragPreview } from './OrderableRowDragPreview.js';
|
|
16
17
|
const baseClass = 'table';
|
|
18
|
+
// A stable id so repeated attempts (e.g. holding Space) update the same toast instead of stacking new ones
|
|
19
|
+
const mustSortByOrderToastId = 'orderable-table-must-sort-by-order';
|
|
17
20
|
export const OrderableTable = ({
|
|
18
21
|
appearance = 'default',
|
|
19
22
|
BeforeTable,
|
|
@@ -32,8 +35,12 @@ export const OrderableTable = ({
|
|
|
32
35
|
const {
|
|
33
36
|
code: localeCode
|
|
34
37
|
} = useLocale();
|
|
38
|
+
const {
|
|
39
|
+
t
|
|
40
|
+
} = useTranslation();
|
|
35
41
|
// Use the data from ListQueryProvider if available, otherwise use the props
|
|
36
42
|
const serverData = listQueryData?.docs || initialData;
|
|
43
|
+
const isOrderable = query.sort === orderableFieldName || query.sort === `-${orderableFieldName}`;
|
|
37
44
|
// Local state to track the current order of rows
|
|
38
45
|
const [localData, setLocalData] = useState(serverData);
|
|
39
46
|
// id -> index for each column
|
|
@@ -54,8 +61,10 @@ export const OrderableTable = ({
|
|
|
54
61
|
moveFromIndex,
|
|
55
62
|
moveToIndex
|
|
56
63
|
}) => {
|
|
57
|
-
if (
|
|
58
|
-
toast.warning('
|
|
64
|
+
if (!isOrderable) {
|
|
65
|
+
toast.warning(t('general:sortByOrderToReorder'), {
|
|
66
|
+
id: mustSortByOrderToastId
|
|
67
|
+
});
|
|
59
68
|
setDragActiveRowId(undefined);
|
|
60
69
|
return;
|
|
61
70
|
}
|
|
@@ -127,6 +136,11 @@ export const OrderableTable = ({
|
|
|
127
136
|
}) => {
|
|
128
137
|
setDragActiveRowId(id);
|
|
129
138
|
};
|
|
139
|
+
const handleDisabledDragAttempt = () => {
|
|
140
|
+
toast.warning(t('general:sortByOrderToReorder'), {
|
|
141
|
+
id: mustSortByOrderToastId
|
|
142
|
+
});
|
|
143
|
+
};
|
|
130
144
|
const rowIds = localData.map(row => row.id ?? row._id);
|
|
131
145
|
return /*#__PURE__*/_jsxs("div", {
|
|
132
146
|
className: [baseClass, appearance && `${baseClass}--appearance-${appearance}`].filter(Boolean).join(' '),
|
|
@@ -146,6 +160,7 @@ export const OrderableTable = ({
|
|
|
146
160
|
})
|
|
147
161
|
}), /*#__PURE__*/_jsx("tbody", {
|
|
148
162
|
children: localData.map((row_0, rowIndex) => /*#__PURE__*/_jsx(DraggableSortableItem, {
|
|
163
|
+
disabled: !isOrderable,
|
|
149
164
|
id: rowIds[rowIndex],
|
|
150
165
|
children: ({
|
|
151
166
|
attributes,
|
|
@@ -160,6 +175,8 @@ export const OrderableTable = ({
|
|
|
160
175
|
columns: activeColumns,
|
|
161
176
|
dragAttributes: attributes,
|
|
162
177
|
dragListeners: listeners,
|
|
178
|
+
isOrderable: isOrderable,
|
|
179
|
+
onDisabledDragAttempt: handleDisabledDragAttempt,
|
|
163
180
|
ref: setNodeRef,
|
|
164
181
|
rowId: row_0.id ?? row_0._id,
|
|
165
182
|
style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderableTable.js","names":["DragOverlay","formatAdminURL","React","useEffect","useState","toast","useConfig","useListQuery","useLocale","DraggableSortableItem","DraggableSortable","OrderableRow","OrderableRowDragPreview","baseClass","OrderableTable","appearance","BeforeTable","collection","columns","data","initialData","config","listQueryData","orderableFieldName","query","code","localeCode","serverData","docs","localData","setLocalData","cellMap","setCellMap","dragActiveRowId","setDragActiveRowId","Object","fromEntries","map","item","index","String","id","_id","activeColumns","filter","col","active","includes","accessor","length","_jsx","handleDragEnd","moveFromIndex","moveToIndex","sort","warning","undefined","movedId","newBeforeRow","newAfterRow","previousData","currentData","newData","splice","target","key","newKeyWillBe","jsonBody","collectionSlug","slug","docsToMove","response","fetch","apiRoute","routes","api","path","body","JSON","stringify","credentials","headers","method","status","Error","ok","json","err","error","message","handleDragStart","rowIds","row","_jsxs","className","Boolean","join","ids","onDragEnd","onDragStart","cellPadding","cellSpacing","i","Heading","rowIndex","children","attributes","isDragging","listeners","setNodeRef","transform","transition","dragAttributes","dragListeners","ref","rowId","style","opacity"],"sources":["../../../src/elements/Table/OrderableTable.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientCollectionConfig, Column, OrderableEndpointBody } from 'payload'\n\nimport './index.css'\n\nimport { DragOverlay } from '@dnd-kit/core'\nimport { formatAdminURL } from 'payload/shared'\nimport React, { useEffect, useState } from 'react'\nimport { toast } from 'sonner'\n\nimport { useConfig } from '../../providers/Config/index.js'\nimport { useListQuery } from '../../providers/ListQuery/index.js'\nimport { useLocale } from '../../providers/Locale/index.js'\nimport { DraggableSortableItem } from '../DraggableSortable/DraggableSortableItem/index.js'\nimport { DraggableSortable } from '../DraggableSortable/index.js'\nimport { OrderableRow } from './OrderableRow.js'\nimport { OrderableRowDragPreview } from './OrderableRowDragPreview.js'\n\nconst baseClass = 'table'\n\nexport type Props = {\n readonly appearance?: 'condensed' | 'default'\n readonly BeforeTable?: React.ReactNode\n readonly collection: ClientCollectionConfig\n readonly columns?: Column[]\n readonly data: Record<string, unknown>[]\n readonly heading?: React.ReactNode\n}\n\nexport const OrderableTable: React.FC<Props> = ({\n appearance = 'default',\n BeforeTable,\n collection,\n columns,\n data: initialData,\n}) => {\n const { config } = useConfig()\n const { data: listQueryData, orderableFieldName, query } = useListQuery()\n const { code: localeCode } = useLocale()\n // Use the data from ListQueryProvider if available, otherwise use the props\n const serverData = listQueryData?.docs || initialData\n\n // Local state to track the current order of rows\n const [localData, setLocalData] = useState(serverData)\n\n // id -> index for each column\n const [cellMap, setCellMap] = useState<Record<string, number>>({})\n\n const [dragActiveRowId, setDragActiveRowId] = useState<number | string | undefined>()\n\n // Update local data when server data changes\n useEffect(() => {\n setLocalData(serverData)\n setCellMap(\n Object.fromEntries(serverData.map((item, index) => [String(item.id ?? item._id), index])),\n )\n }, [serverData])\n\n const activeColumns = columns?.filter((col) => col?.active)\n\n if (\n !activeColumns ||\n activeColumns.filter((col) => !['_dragHandle', '_select'].includes(col.accessor)).length === 0\n ) {\n return <div>No columns selected</div>\n }\n\n const handleDragEnd = async ({ moveFromIndex, moveToIndex }) => {\n if (query.sort !== orderableFieldName && query.sort !== `-${orderableFieldName}`) {\n toast.warning('To reorder the rows you must first sort them by the \"Order\" column')\n setDragActiveRowId(undefined)\n return\n }\n\n if (moveFromIndex === moveToIndex) {\n setDragActiveRowId(undefined)\n return\n }\n\n const movedId = localData[moveFromIndex].id ?? localData[moveFromIndex]._id\n const newBeforeRow =\n moveToIndex > moveFromIndex ? localData[moveToIndex] : localData[moveToIndex - 1]\n const newAfterRow =\n moveToIndex > moveFromIndex ? localData[moveToIndex + 1] : localData[moveToIndex]\n\n // Store the original data for rollback\n const previousData = [...localData]\n\n // Optimisitc update of local state to reorder the rows\n setLocalData((currentData) => {\n const newData = [...currentData]\n // Update the rendered cell for the moved row to show \"pending\"\n newData[moveFromIndex][orderableFieldName] = `pending`\n // Move the item in the array\n newData.splice(moveToIndex, 0, newData.splice(moveFromIndex, 1)[0])\n return newData\n })\n\n try {\n const target: OrderableEndpointBody['target'] = newBeforeRow\n ? {\n id: newBeforeRow.id ?? newBeforeRow._id,\n key: newBeforeRow[orderableFieldName],\n }\n : {\n id: newAfterRow.id ?? newAfterRow._id,\n key: newAfterRow[orderableFieldName],\n }\n\n const newKeyWillBe =\n (newBeforeRow && query.sort === orderableFieldName) ||\n (!newBeforeRow && query.sort === `-${orderableFieldName}`)\n ? 'greater'\n : 'less'\n\n const jsonBody: OrderableEndpointBody = {\n collectionSlug: collection.slug,\n docsToMove: [movedId],\n newKeyWillBe,\n orderableFieldName,\n target,\n }\n\n const response = await fetch(\n formatAdminURL({\n apiRoute: config.routes.api,\n path: `/reorder?locale=${localeCode}`,\n }),\n {\n body: JSON.stringify(jsonBody),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n },\n )\n\n if (response.status === 403) {\n throw new Error('You do not have permission to reorder these rows')\n }\n\n if (!response.ok) {\n throw new Error(\n 'Failed to reorder. This can happen if you reorder several rows too quickly. Please try again.',\n )\n }\n\n if (response.status === 200 && (await response.json())['message'] === 'initial migration') {\n throw new Error(\n 'You have enabled \"orderable\" on a collection with existing documents' +\n 'and this is the first time you have sorted documents. We have run an automatic migration ' +\n 'to add an initial order to the documents. Please refresh the page and try again.',\n )\n }\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err)\n // Rollback to previous state if the request fails\n setLocalData(previousData)\n toast.error(error)\n } finally {\n setDragActiveRowId(undefined)\n }\n }\n\n const handleDragStart = ({ id }) => {\n setDragActiveRowId(id)\n }\n\n const rowIds = localData.map((row) => row.id ?? row._id)\n\n return (\n <div\n className={[baseClass, appearance && `${baseClass}--appearance-${appearance}`]\n .filter(Boolean)\n .join(' ')}\n >\n {BeforeTable}\n <DraggableSortable ids={rowIds} onDragEnd={handleDragEnd} onDragStart={handleDragStart}>\n <table cellPadding=\"0\" cellSpacing=\"0\">\n <thead>\n <tr>\n {activeColumns.map((col, i) => (\n <th id={`heading-${col.accessor}`} key={i}>\n {col.Heading}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {localData.map((row, rowIndex) => (\n <DraggableSortableItem id={rowIds[rowIndex]} key={rowIds[rowIndex]}>\n {({ attributes, isDragging, listeners, setNodeRef, transform, transition }) => (\n <OrderableRow\n cellMap={cellMap}\n className={`row-${rowIndex + 1}`}\n columns={activeColumns}\n dragAttributes={attributes}\n dragListeners={listeners}\n ref={setNodeRef}\n rowId={row.id ?? row._id}\n style={{\n opacity: isDragging ? 0 : 1,\n transform,\n transition,\n }}\n />\n )}\n </DraggableSortableItem>\n ))}\n </tbody>\n </table>\n\n <DragOverlay>\n <OrderableRowDragPreview\n className={[baseClass, `${baseClass}--drag-preview`].join(' ')}\n rowId={dragActiveRowId}\n >\n <OrderableRow cellMap={cellMap} columns={activeColumns} rowId={dragActiveRowId} />\n </OrderableRowDragPreview>\n </DragOverlay>\n </DraggableSortable>\n </div>\n )\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"OrderableTable.js","names":["DragOverlay","formatAdminURL","React","useEffect","useState","toast","useConfig","useListQuery","useLocale","useTranslation","DraggableSortableItem","DraggableSortable","OrderableRow","OrderableRowDragPreview","baseClass","mustSortByOrderToastId","OrderableTable","appearance","BeforeTable","collection","columns","data","initialData","config","listQueryData","orderableFieldName","query","code","localeCode","t","serverData","docs","isOrderable","sort","localData","setLocalData","cellMap","setCellMap","dragActiveRowId","setDragActiveRowId","Object","fromEntries","map","item","index","String","id","_id","activeColumns","filter","col","active","includes","accessor","length","_jsx","handleDragEnd","moveFromIndex","moveToIndex","warning","undefined","movedId","newBeforeRow","newAfterRow","previousData","currentData","newData","splice","target","key","newKeyWillBe","jsonBody","collectionSlug","slug","docsToMove","response","fetch","apiRoute","routes","api","path","body","JSON","stringify","credentials","headers","method","status","Error","ok","json","err","error","message","handleDragStart","handleDisabledDragAttempt","rowIds","row","_jsxs","className","Boolean","join","ids","onDragEnd","onDragStart","cellPadding","cellSpacing","i","Heading","rowIndex","disabled","children","attributes","isDragging","listeners","setNodeRef","transform","transition","dragAttributes","dragListeners","onDisabledDragAttempt","ref","rowId","style","opacity"],"sources":["../../../src/elements/Table/OrderableTable.tsx"],"sourcesContent":["'use client'\n\nimport type { ClientCollectionConfig, Column, OrderableEndpointBody } from 'payload'\n\nimport './index.css'\n\nimport { DragOverlay } from '@dnd-kit/core'\nimport { formatAdminURL } from 'payload/shared'\nimport React, { useEffect, useState } from 'react'\nimport { toast } from 'sonner'\n\nimport { useConfig } from '../../providers/Config/index.js'\nimport { useListQuery } from '../../providers/ListQuery/index.js'\nimport { useLocale } from '../../providers/Locale/index.js'\nimport { useTranslation } from '../../providers/Translation/index.js'\nimport { DraggableSortableItem } from '../DraggableSortable/DraggableSortableItem/index.js'\nimport { DraggableSortable } from '../DraggableSortable/index.js'\nimport { OrderableRow } from './OrderableRow.js'\nimport { OrderableRowDragPreview } from './OrderableRowDragPreview.js'\n\nconst baseClass = 'table'\n\n// A stable id so repeated attempts (e.g. holding Space) update the same toast instead of stacking new ones\nconst mustSortByOrderToastId = 'orderable-table-must-sort-by-order'\n\nexport type Props = {\n readonly appearance?: 'condensed' | 'default'\n readonly BeforeTable?: React.ReactNode\n readonly collection: ClientCollectionConfig\n readonly columns?: Column[]\n readonly data: Record<string, unknown>[]\n readonly heading?: React.ReactNode\n}\n\nexport const OrderableTable: React.FC<Props> = ({\n appearance = 'default',\n BeforeTable,\n collection,\n columns,\n data: initialData,\n}) => {\n const { config } = useConfig()\n const { data: listQueryData, orderableFieldName, query } = useListQuery()\n const { code: localeCode } = useLocale()\n const { t } = useTranslation()\n // Use the data from ListQueryProvider if available, otherwise use the props\n const serverData = listQueryData?.docs || initialData\n\n const isOrderable = query.sort === orderableFieldName || query.sort === `-${orderableFieldName}`\n\n // Local state to track the current order of rows\n const [localData, setLocalData] = useState(serverData)\n\n // id -> index for each column\n const [cellMap, setCellMap] = useState<Record<string, number>>({})\n\n const [dragActiveRowId, setDragActiveRowId] = useState<number | string | undefined>()\n\n // Update local data when server data changes\n useEffect(() => {\n setLocalData(serverData)\n setCellMap(\n Object.fromEntries(serverData.map((item, index) => [String(item.id ?? item._id), index])),\n )\n }, [serverData])\n\n const activeColumns = columns?.filter((col) => col?.active)\n\n if (\n !activeColumns ||\n activeColumns.filter((col) => !['_dragHandle', '_select'].includes(col.accessor)).length === 0\n ) {\n return <div>No columns selected</div>\n }\n\n const handleDragEnd = async ({ moveFromIndex, moveToIndex }) => {\n if (!isOrderable) {\n toast.warning(t('general:sortByOrderToReorder'), { id: mustSortByOrderToastId })\n setDragActiveRowId(undefined)\n return\n }\n\n if (moveFromIndex === moveToIndex) {\n setDragActiveRowId(undefined)\n return\n }\n\n const movedId = localData[moveFromIndex].id ?? localData[moveFromIndex]._id\n const newBeforeRow =\n moveToIndex > moveFromIndex ? localData[moveToIndex] : localData[moveToIndex - 1]\n const newAfterRow =\n moveToIndex > moveFromIndex ? localData[moveToIndex + 1] : localData[moveToIndex]\n\n // Store the original data for rollback\n const previousData = [...localData]\n\n // Optimisitc update of local state to reorder the rows\n setLocalData((currentData) => {\n const newData = [...currentData]\n // Update the rendered cell for the moved row to show \"pending\"\n newData[moveFromIndex][orderableFieldName] = `pending`\n // Move the item in the array\n newData.splice(moveToIndex, 0, newData.splice(moveFromIndex, 1)[0])\n return newData\n })\n\n try {\n const target: OrderableEndpointBody['target'] = newBeforeRow\n ? {\n id: newBeforeRow.id ?? newBeforeRow._id,\n key: newBeforeRow[orderableFieldName],\n }\n : {\n id: newAfterRow.id ?? newAfterRow._id,\n key: newAfterRow[orderableFieldName],\n }\n\n const newKeyWillBe =\n (newBeforeRow && query.sort === orderableFieldName) ||\n (!newBeforeRow && query.sort === `-${orderableFieldName}`)\n ? 'greater'\n : 'less'\n\n const jsonBody: OrderableEndpointBody = {\n collectionSlug: collection.slug,\n docsToMove: [movedId],\n newKeyWillBe,\n orderableFieldName,\n target,\n }\n\n const response = await fetch(\n formatAdminURL({\n apiRoute: config.routes.api,\n path: `/reorder?locale=${localeCode}`,\n }),\n {\n body: JSON.stringify(jsonBody),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n },\n )\n\n if (response.status === 403) {\n throw new Error('You do not have permission to reorder these rows')\n }\n\n if (!response.ok) {\n throw new Error(\n 'Failed to reorder. This can happen if you reorder several rows too quickly. Please try again.',\n )\n }\n\n if (response.status === 200 && (await response.json())['message'] === 'initial migration') {\n throw new Error(\n 'You have enabled \"orderable\" on a collection with existing documents' +\n 'and this is the first time you have sorted documents. We have run an automatic migration ' +\n 'to add an initial order to the documents. Please refresh the page and try again.',\n )\n }\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err)\n // Rollback to previous state if the request fails\n setLocalData(previousData)\n toast.error(error)\n } finally {\n setDragActiveRowId(undefined)\n }\n }\n\n const handleDragStart = ({ id }) => {\n setDragActiveRowId(id)\n }\n\n const handleDisabledDragAttempt = () => {\n toast.warning(t('general:sortByOrderToReorder'), { id: mustSortByOrderToastId })\n }\n\n const rowIds = localData.map((row) => row.id ?? row._id)\n\n return (\n <div\n className={[baseClass, appearance && `${baseClass}--appearance-${appearance}`]\n .filter(Boolean)\n .join(' ')}\n >\n {BeforeTable}\n <DraggableSortable ids={rowIds} onDragEnd={handleDragEnd} onDragStart={handleDragStart}>\n <table cellPadding=\"0\" cellSpacing=\"0\">\n <thead>\n <tr>\n {activeColumns.map((col, i) => (\n <th id={`heading-${col.accessor}`} key={i}>\n {col.Heading}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {localData.map((row, rowIndex) => (\n <DraggableSortableItem\n disabled={!isOrderable}\n id={rowIds[rowIndex]}\n key={rowIds[rowIndex]}\n >\n {({ attributes, isDragging, listeners, setNodeRef, transform, transition }) => (\n <OrderableRow\n cellMap={cellMap}\n className={`row-${rowIndex + 1}`}\n columns={activeColumns}\n dragAttributes={attributes}\n dragListeners={listeners}\n isOrderable={isOrderable}\n onDisabledDragAttempt={handleDisabledDragAttempt}\n ref={setNodeRef}\n rowId={row.id ?? row._id}\n style={{\n opacity: isDragging ? 0 : 1,\n transform,\n transition,\n }}\n />\n )}\n </DraggableSortableItem>\n ))}\n </tbody>\n </table>\n\n <DragOverlay>\n <OrderableRowDragPreview\n className={[baseClass, `${baseClass}--drag-preview`].join(' ')}\n rowId={dragActiveRowId}\n >\n <OrderableRow cellMap={cellMap} columns={activeColumns} rowId={dragActiveRowId} />\n </OrderableRowDragPreview>\n </DragOverlay>\n </DraggableSortable>\n </div>\n )\n}\n"],"mappings":"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","ignoreList":[]}
|