payload 1.2.1 → 1.2.2

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +40 -2
  2. package/components/fields/Array.ts +1 -2
  3. package/components/fields/Blocks.ts +1 -2
  4. package/components/fields/Cell.ts +1 -1
  5. package/components/fields/Checkbox.ts +1 -2
  6. package/components/fields/Code.ts +1 -2
  7. package/components/fields/DateTime.ts +1 -2
  8. package/components/fields/Email.ts +1 -2
  9. package/components/fields/Group.ts +1 -2
  10. package/components/fields/Number.ts +1 -2
  11. package/components/fields/Password.ts +1 -2
  12. package/components/fields/RadioGroup/RadioInput.ts +1 -2
  13. package/components/fields/RadioGroup/index.ts +1 -2
  14. package/components/fields/Relationship.ts +1 -2
  15. package/components/fields/RichText.ts +1 -2
  16. package/components/fields/Row.ts +1 -2
  17. package/components/fields/Select.ts +1 -2
  18. package/components/fields/Text.ts +1 -2
  19. package/components/fields/Textarea.ts +1 -2
  20. package/components/fields/Upload.ts +1 -2
  21. package/components/views/Cell.ts +1 -2
  22. package/components/views/Edit.ts +1 -2
  23. package/components/views/List.ts +1 -2
  24. package/dist/admin/components/elements/ReactSelect/index.js +3 -3
  25. package/dist/admin/components/elements/ReactSelect/types.d.ts +11 -10
  26. package/dist/admin/components/elements/Status/index.js +1 -1
  27. package/dist/admin/components/forms/Form/context.d.ts +15 -0
  28. package/dist/admin/components/forms/Form/context.js +16 -1
  29. package/dist/admin/components/forms/field-types/Blocks/SectionTitle/index.scss +3 -2
  30. package/dist/admin/components/forms/field-types/Blocks/index.js +1 -1
  31. package/dist/admin/components/forms/field-types/Code/Code.js +3 -4
  32. package/dist/admin/components/forms/field-types/Code/index.scss +177 -78
  33. package/dist/admin/components/forms/field-types/Relationship/AddNew/index.js +0 -1
  34. package/dist/admin/components/forms/field-types/Relationship/GetFilterOptions/index.d.ts +11 -0
  35. package/dist/admin/components/forms/field-types/Relationship/GetFilterOptions/index.js +37 -0
  36. package/dist/admin/components/forms/field-types/Relationship/findOptionsByValue.d.ts +8 -0
  37. package/dist/admin/components/forms/field-types/Relationship/findOptionsByValue.js +29 -0
  38. package/dist/admin/components/forms/field-types/Relationship/index.js +89 -139
  39. package/dist/admin/components/forms/field-types/Relationship/optionsReducer.d.ts +2 -2
  40. package/dist/admin/components/forms/field-types/Relationship/optionsReducer.js +2 -29
  41. package/dist/admin/components/forms/field-types/Relationship/types.d.ts +14 -2
  42. package/dist/admin/components/forms/field-types/RichText/elements/link/Element.js +3 -3
  43. package/dist/admin/components/forms/field-types/RichText/elements/link/index.scss +2 -1
  44. package/dist/admin/components/forms/field-types/RichText/elements/upload/Button/index.js +1 -1
  45. package/dist/admin/components/forms/field-types/Row/index.js +1 -1
  46. package/dist/admin/components/forms/field-types/Select/Input.d.ts +2 -2
  47. package/dist/admin/components/forms/useField/index.d.ts +5 -0
  48. package/dist/admin/components/forms/useField/index.js +5 -0
  49. package/dist/admin/components/utilities/I18n/index.js +1 -1
  50. package/dist/admin/components/views/Global/index.scss +31 -24
  51. package/dist/admin/components/views/collections/Edit/index.scss +19 -4
  52. package/dist/collections/buildSchema.js +5 -1
  53. package/dist/collections/config/types.d.ts +16 -3
  54. package/dist/collections/operations/find.js +11 -6
  55. package/dist/collections/operations/findVersions.js +4 -5
  56. package/dist/collections/operations/update.js +8 -9
  57. package/dist/collections/requestHandlers/find.js +4 -3
  58. package/dist/config/schema.js +2 -2
  59. package/dist/config/types.d.ts +269 -6
  60. package/dist/config/types.js +1 -1
  61. package/dist/email/build.js +2 -2
  62. package/dist/express/types.d.ts +26 -2
  63. package/dist/fields/config/sanitize.spec.js +5 -5
  64. package/dist/fields/config/types.d.ts +3 -3
  65. package/dist/fields/hooks/afterRead/relationshipPopulationPromise.js +73 -21
  66. package/dist/globals/operations/findVersions.js +4 -5
  67. package/dist/globals/operations/update.js +4 -4
  68. package/dist/graphql/schema/fieldToWhereInputSchemaMap.js +7 -7
  69. package/dist/graphql/schema/operators.d.ts +1 -0
  70. package/dist/graphql/schema/operators.js +2 -1
  71. package/dist/mongoose/types.d.ts +3 -3
  72. package/dist/translations/de.json +100 -100
  73. package/dist/translations/en.json +1 -1
  74. package/dist/translations/es.json +1 -1
  75. package/dist/translations/fr.json +1 -1
  76. package/dist/translations/it.json +1 -1
  77. package/dist/translations/my.json +1 -1
  78. package/dist/translations/nl.json +1 -1
  79. package/dist/translations/pl.json +1 -1
  80. package/dist/translations/pt.json +1 -1
  81. package/dist/translations/ru.json +1 -1
  82. package/dist/translations/sv.json +1 -1
  83. package/dist/translations/vi.json +1 -1
  84. package/dist/utilities/isNumber.d.ts +1 -0
  85. package/dist/utilities/isNumber.js +8 -0
  86. package/dist/versions/getLatestCollectionVersion.d.ts +10 -0
  87. package/dist/versions/getLatestCollectionVersion.js +27 -0
  88. package/dist/versions/getLatestGlobalVersion.d.ts +10 -0
  89. package/dist/versions/getLatestGlobalVersion.js +27 -0
  90. package/docs/admin/components.mdx +1 -5
  91. package/docs/configuration/globals.mdx +12 -12
  92. package/docs/fields/array.mdx +14 -14
  93. package/docs/fields/blocks.mdx +1 -1
  94. package/docs/fields/checkbox.mdx +2 -2
  95. package/docs/fields/code.mdx +1 -1
  96. package/docs/fields/date.mdx +1 -1
  97. package/docs/fields/email.mdx +1 -1
  98. package/docs/fields/number.mdx +1 -1
  99. package/docs/fields/radio.mdx +1 -1
  100. package/docs/fields/relationship.mdx +1 -1
  101. package/docs/fields/rich-text.mdx +1 -1
  102. package/docs/fields/select.mdx +2 -2
  103. package/docs/fields/text.mdx +2 -2
  104. package/docs/fields/textarea.mdx +1 -1
  105. package/docs/fields/upload.mdx +51 -17
  106. package/docs/production/preventing-abuse.mdx +6 -6
  107. package/package.json +3 -2
@@ -1,5 +1,4 @@
1
1
  import React, { useCallback, useEffect, useState, useReducer, useRef, } from 'react';
2
- import equal from 'deep-equal';
3
2
  import qs from 'qs';
4
3
  import { useTranslation } from 'react-i18next';
5
4
  import { useConfig } from '../../../utilities/Config';
@@ -11,16 +10,14 @@ import Label from '../../Label';
11
10
  import Error from '../../Error';
12
11
  import FieldDescription from '../../FieldDescription';
13
12
  import { relationship } from '../../../../../fields/validations';
14
- import { useFormProcessing, useAllFormFields } from '../../Form/context';
13
+ import { useFormProcessing } from '../../Form/context';
15
14
  import optionsReducer from './optionsReducer';
16
15
  import { createRelationMap } from './createRelationMap';
17
16
  import { useDebouncedCallback } from '../../../../hooks/useDebouncedCallback';
18
- import { useDocumentInfo } from '../../../utilities/DocumentInfo';
19
- import { getFilterOptionsQuery } from '../getFilterOptionsQuery';
20
17
  import wordBoundariesRegex from '../../../../../utilities/wordBoundariesRegex';
21
- import reduceFieldsToValues from '../../Form/reduceFieldsToValues';
22
- import getSiblingData from '../../Form/getSiblingData';
23
18
  import { AddNewRelation } from './AddNew';
19
+ import { findOptionsByValue } from './findOptionsByValue';
20
+ import { GetFilterOptions } from './GetFilterOptions';
24
21
  import './index.scss';
25
22
  const maxResultsPerRequest = 10;
26
23
  const baseClass = 'relationship';
@@ -28,21 +25,19 @@ const Relationship = (props) => {
28
25
  const { relationTo, validate = relationship, path, name, required, label, hasMany, filterOptions, admin: { readOnly, style, className, width, description, condition, isSortable = true, } = {}, } = props;
29
26
  const { serverURL, routes: { api, }, collections, } = useConfig();
30
27
  const { t, i18n } = useTranslation('fields');
31
- const { id } = useDocumentInfo();
32
- const { user, permissions } = useAuth();
33
- const [fields] = useAllFormFields();
28
+ const { permissions } = useAuth();
34
29
  const formProcessing = useFormProcessing();
35
30
  const hasMultipleRelations = Array.isArray(relationTo);
36
- const [options, dispatchOptions] = useReducer(optionsReducer, required || hasMany ? [] : [{ value: null, label: t('general:none') }]);
31
+ const [options, dispatchOptions] = useReducer(optionsReducer, []);
37
32
  const [lastFullyLoadedRelation, setLastFullyLoadedRelation] = useState(-1);
38
33
  const [lastLoadedPage, setLastLoadedPage] = useState(1);
39
34
  const [errorLoading, setErrorLoading] = useState('');
40
- const [optionFilters, setOptionFilters] = useState();
41
- const [hasLoadedValueOptions, setHasLoadedValueOptions] = useState(false);
35
+ const [filterOptionsResult, setFilterOptionsResult] = useState();
42
36
  const [search, setSearch] = useState('');
37
+ const [isLoading, setIsLoading] = useState(false);
38
+ const [hasLoadedFirstPage, setHasLoadedFirstPage] = useState(false);
43
39
  const [enableWordBoundarySearch, setEnableWordBoundarySearch] = useState(false);
44
40
  const firstRun = useRef(true);
45
- const fieldsRef = useRef(fields);
46
41
  const memoizedValidate = useCallback((value, validationOptions) => {
47
42
  return validate(value, { ...validationOptions, required });
48
43
  }, [validate, required]);
@@ -51,13 +46,7 @@ const Relationship = (props) => {
51
46
  validate: memoizedValidate,
52
47
  condition,
53
48
  });
54
- const getFormData = useCallback(() => {
55
- return [
56
- reduceFieldsToValues(fieldsRef.current, true),
57
- getSiblingData(fieldsRef.current, path),
58
- ];
59
- }, [fieldsRef, path]);
60
- const getResults = useCallback(async ({ lastFullyLoadedRelation: lastFullyLoadedRelationArg, lastLoadedPage: lastLoadedPageArg, search: searchArg, value: valueArg, sort, }) => {
49
+ const getResults = useCallback(async ({ lastFullyLoadedRelation: lastFullyLoadedRelationArg, lastLoadedPage: lastLoadedPageArg, search: searchArg, value: valueArg, sort, onSuccess, }) => {
61
50
  if (!permissions) {
62
51
  return;
63
52
  }
@@ -96,8 +85,8 @@ const Relationship = (props) => {
96
85
  },
97
86
  });
98
87
  }
99
- if (optionFilters === null || optionFilters === void 0 ? void 0 : optionFilters[relation]) {
100
- query.where.and.push(optionFilters[relation]);
88
+ if (filterOptionsResult === null || filterOptionsResult === void 0 ? void 0 : filterOptionsResult[relation]) {
89
+ query.where.and.push(filterOptionsResult[relation]);
101
90
  }
102
91
  const response = await fetch(`${serverURL}${api}/${relation}?${qs.stringify(query)}`, {
103
92
  credentials: 'include',
@@ -109,7 +98,7 @@ const Relationship = (props) => {
109
98
  const data = await response.json();
110
99
  if (data.docs.length > 0) {
111
100
  resultsFetched += data.docs.length;
112
- dispatchOptions({ type: 'ADD', docs: data.docs, hasMultipleRelations, collection, sort, i18n });
101
+ dispatchOptions({ type: 'ADD', docs: data.docs, collection, sort, i18n });
113
102
  setLastLoadedPage(data.page);
114
103
  if (!data.nextPage) {
115
104
  setLastFullyLoadedRelation(relations.indexOf(relation));
@@ -124,13 +113,15 @@ const Relationship = (props) => {
124
113
  else if (response.status === 403) {
125
114
  setLastFullyLoadedRelation(relations.indexOf(relation));
126
115
  lastLoadedPageToUse = 1;
127
- dispatchOptions({ type: 'ADD', docs: [], hasMultipleRelations, collection, sort, ids: relationMap[relation], i18n });
116
+ dispatchOptions({ type: 'ADD', docs: [], collection, sort, ids: relationMap[relation], i18n });
128
117
  }
129
118
  else {
130
119
  setErrorLoading(t('error:unspecific'));
131
120
  }
132
121
  }
133
122
  }, Promise.resolve());
123
+ if (typeof onSuccess === 'function')
124
+ onSuccess();
134
125
  }
135
126
  }, [
136
127
  permissions,
@@ -138,58 +129,12 @@ const Relationship = (props) => {
138
129
  hasMany,
139
130
  errorLoading,
140
131
  collections,
141
- optionFilters,
132
+ filterOptionsResult,
142
133
  serverURL,
143
134
  api,
144
- hasMultipleRelations,
145
135
  t,
146
136
  i18n,
147
137
  ]);
148
- const findOptionsByValue = useCallback(() => {
149
- if (value) {
150
- if (hasMany) {
151
- if (Array.isArray(value)) {
152
- return value.map((val) => {
153
- if (hasMultipleRelations) {
154
- let matchedOption;
155
- options.forEach((opt) => {
156
- if (opt.options) {
157
- opt.options.some((subOpt) => {
158
- if ((subOpt === null || subOpt === void 0 ? void 0 : subOpt.value) === val.value) {
159
- matchedOption = subOpt;
160
- return true;
161
- }
162
- return false;
163
- });
164
- }
165
- });
166
- return matchedOption;
167
- }
168
- return options.find((opt) => opt.value === val);
169
- });
170
- }
171
- return undefined;
172
- }
173
- if (hasMultipleRelations) {
174
- let matchedOption;
175
- const valueWithRelation = value;
176
- options.forEach((opt) => {
177
- if (opt === null || opt === void 0 ? void 0 : opt.options) {
178
- opt.options.some((subOpt) => {
179
- if ((subOpt === null || subOpt === void 0 ? void 0 : subOpt.value) === valueWithRelation.value) {
180
- matchedOption = subOpt;
181
- return true;
182
- }
183
- return false;
184
- });
185
- }
186
- });
187
- return matchedOption;
188
- }
189
- return options.find((opt) => opt.value === value);
190
- }
191
- return undefined;
192
- }, [hasMany, hasMultipleRelations, value, options]);
193
138
  const updateSearch = useDebouncedCallback((searchArg, valueArg) => {
194
139
  getResults({ search: searchArg, value: valueArg, sort: true });
195
140
  setSearch(searchArg);
@@ -199,72 +144,60 @@ const Relationship = (props) => {
199
144
  updateSearch(searchArg, valueArg);
200
145
  }
201
146
  }, [search, updateSearch]);
202
- // ///////////////////////////
203
- // Fetch value options when initialValue changes
204
- // ///////////////////////////
147
+ // ///////////////////////////////////
148
+ // Ensure we have an option for each value
149
+ // ///////////////////////////////////
205
150
  useEffect(() => {
206
- if (initialValue && !hasLoadedValueOptions) {
207
- const relationMap = createRelationMap({
208
- hasMany,
209
- relationTo,
210
- value: initialValue,
151
+ const relationMap = createRelationMap({
152
+ hasMany,
153
+ relationTo,
154
+ value,
155
+ });
156
+ Object.entries(relationMap).reduce(async (priorRelation, [relation, ids]) => {
157
+ await priorRelation;
158
+ const idsToLoad = ids.filter((id) => {
159
+ return !options.find((optionGroup) => { var _a; return (_a = optionGroup === null || optionGroup === void 0 ? void 0 : optionGroup.options) === null || _a === void 0 ? void 0 : _a.find((option) => option.value === id && option.relationTo === relation); });
211
160
  });
212
- Object.entries(relationMap).reduce(async (priorRelation, [relation, ids]) => {
213
- await priorRelation;
214
- if (ids.length > 0) {
215
- const query = {
216
- where: {
217
- id: {
218
- in: ids,
219
- },
161
+ if (idsToLoad.length > 0) {
162
+ const query = {
163
+ where: {
164
+ id: {
165
+ in: idsToLoad,
220
166
  },
221
- depth: 0,
222
- limit: ids.length,
223
- };
224
- if (!errorLoading) {
225
- const response = await fetch(`${serverURL}${api}/${relation}?${qs.stringify(query)}`, {
226
- credentials: 'include',
227
- headers: {
228
- 'Accept-Language': i18n.language,
229
- },
230
- });
231
- const collection = collections.find((coll) => coll.slug === relation);
232
- if (response.ok) {
233
- const data = await response.json();
234
- dispatchOptions({ type: 'ADD', docs: data.docs, hasMultipleRelations, collection, sort: true, ids, i18n });
235
- }
236
- else if (response.status === 403) {
237
- dispatchOptions({ type: 'ADD', docs: [], hasMultipleRelations, collection, sort: true, ids, i18n });
238
- }
167
+ },
168
+ depth: 0,
169
+ limit: idsToLoad.length,
170
+ };
171
+ if (!errorLoading) {
172
+ const response = await fetch(`${serverURL}${api}/${relation}?${qs.stringify(query)}`, {
173
+ credentials: 'include',
174
+ headers: {
175
+ 'Accept-Language': i18n.language,
176
+ },
177
+ });
178
+ const collection = collections.find((coll) => coll.slug === relation);
179
+ if (response.ok) {
180
+ const data = await response.json();
181
+ dispatchOptions({ type: 'ADD', docs: data.docs, collection, sort: true, ids: idsToLoad, i18n });
182
+ }
183
+ else if (response.status === 403) {
184
+ dispatchOptions({ type: 'ADD', docs: [], collection, sort: true, ids: idsToLoad, i18n });
239
185
  }
240
186
  }
241
- }, Promise.resolve());
242
- setHasLoadedValueOptions(true);
243
- }
244
- }, [hasMany, hasMultipleRelations, relationTo, initialValue, hasLoadedValueOptions, errorLoading, collections, api, serverURL, i18n]);
245
- useEffect(() => {
246
- if (!filterOptions)
247
- return;
248
- const [data, siblingData] = getFormData();
249
- const newOptionFilters = getFilterOptionsQuery(filterOptions, {
250
- id,
251
- data,
252
- relationTo,
253
- siblingData,
254
- user,
255
- });
256
- if (!equal(newOptionFilters, optionFilters)) {
257
- setOptionFilters(newOptionFilters);
258
- }
259
- }, [relationTo, filterOptions, optionFilters, id, getFormData, path, user]);
260
- useEffect(() => {
261
- if (optionFilters || !filterOptions) {
262
- setHasLoadedValueOptions(false);
263
- getResults({
264
- value: initialValue,
265
- });
266
- }
267
- }, [initialValue, getResults, optionFilters, filterOptions]);
187
+ }
188
+ }, Promise.resolve());
189
+ }, [
190
+ options,
191
+ value,
192
+ hasMany,
193
+ errorLoading,
194
+ collections,
195
+ hasMultipleRelations,
196
+ serverURL,
197
+ api,
198
+ i18n,
199
+ relationTo,
200
+ ]);
268
201
  // Determine if we should switch to word boundary search
269
202
  useEffect(() => {
270
203
  const relations = Array.isArray(relationTo) ? relationTo : [relationTo];
@@ -276,7 +209,7 @@ const Relationship = (props) => {
276
209
  }, true);
277
210
  setEnableWordBoundarySearch(!isIdOnly);
278
211
  }, [relationTo, collections]);
279
- // When relationTo changes, reset relationship options
212
+ // When relationTo or filterOptionsResult changes, reset component
280
213
  // Note - effect should not run on first run
281
214
  useEffect(() => {
282
215
  if (firstRun.current) {
@@ -284,8 +217,10 @@ const Relationship = (props) => {
284
217
  return;
285
218
  }
286
219
  dispatchOptions({ type: 'CLEAR' });
287
- setHasLoadedValueOptions(false);
288
- }, [relationTo]);
220
+ setLastFullyLoadedRelation(-1);
221
+ setLastLoadedPage(1);
222
+ setHasLoadedFirstPage(false);
223
+ }, [relationTo, filterOptionsResult]);
289
224
  const classes = [
290
225
  'field-type',
291
226
  baseClass,
@@ -294,8 +229,8 @@ const Relationship = (props) => {
294
229
  errorLoading && 'error-loading',
295
230
  readOnly && `${baseClass}--read-only`,
296
231
  ].filter(Boolean).join(' ');
297
- const valueToRender = (findOptionsByValue() || value);
298
- if ((valueToRender === null || valueToRender === void 0 ? void 0 : valueToRender.value) === 'null')
232
+ const valueToRender = findOptionsByValue({ value, options });
233
+ if (!Array.isArray(valueToRender) && (valueToRender === null || valueToRender === void 0 ? void 0 : valueToRender.value) === 'null')
299
234
  valueToRender.value = null;
300
235
  return (React.createElement("div", { id: `field-${(path || name).replace(/\./gi, '__')}`, className: classes, style: {
301
236
  ...style,
@@ -303,9 +238,13 @@ const Relationship = (props) => {
303
238
  } },
304
239
  React.createElement(Error, { showError: showError, message: errorMessage }),
305
240
  React.createElement(Label, { htmlFor: path, label: label, required: required }),
241
+ React.createElement(GetFilterOptions, { ...{ filterOptionsResult, setFilterOptionsResult, filterOptions, path, relationTo } }),
306
242
  !errorLoading && (React.createElement("div", { className: `${baseClass}__wrap` },
307
243
  React.createElement(ReactSelect, { isDisabled: readOnly, onInputChange: (newSearch) => handleInputChange(newSearch, value), onChange: !readOnly ? (selected) => {
308
- if (hasMany) {
244
+ if (selected === null) {
245
+ setValue(hasMany ? [] : null);
246
+ }
247
+ else if (hasMany) {
309
248
  setValue(selected ? selected.map((option) => {
310
249
  if (hasMultipleRelations) {
311
250
  return {
@@ -333,7 +272,18 @@ const Relationship = (props) => {
333
272
  value: initialValue,
334
273
  sort: false,
335
274
  });
336
- }, value: valueToRender, showError: showError, disabled: formProcessing, options: options, isMulti: hasMany, isSortable: isSortable, filterOption: enableWordBoundarySearch ? (item, searchFilter) => {
275
+ }, value: valueToRender, showError: showError, disabled: formProcessing, options: options, isMulti: hasMany, isSortable: isSortable, isLoading: isLoading, onMenuOpen: () => {
276
+ if (!hasLoadedFirstPage) {
277
+ setIsLoading(true);
278
+ getResults({
279
+ value: initialValue,
280
+ onSuccess: () => {
281
+ setHasLoadedFirstPage(true);
282
+ setIsLoading(false);
283
+ },
284
+ });
285
+ }
286
+ }, filterOption: enableWordBoundarySearch ? (item, searchFilter) => {
337
287
  const r = wordBoundariesRegex(searchFilter || '');
338
288
  return r.test(item.label);
339
289
  } : undefined }),
@@ -1,3 +1,3 @@
1
- import { Option, Action } from './types';
2
- declare const optionsReducer: (state: Option[], action: Action) => Option[];
1
+ import { Action, OptionGroup } from './types';
2
+ declare const optionsReducer: (state: OptionGroup[], action: Action) => OptionGroup[];
3
3
  export default optionsReducer;
@@ -24,37 +24,10 @@ const optionsReducer = (state, action) => {
24
24
  return [];
25
25
  }
26
26
  case 'ADD': {
27
- const { hasMultipleRelations, collection, docs, sort, ids = [], i18n } = action;
27
+ const { collection, docs, sort, ids = [], i18n } = action;
28
28
  const relation = collection.slug;
29
29
  const labelKey = collection.admin.useAsTitle || 'id';
30
30
  const loadedIDs = reduceToIDs(state);
31
- if (!hasMultipleRelations) {
32
- const options = [
33
- ...state,
34
- ...docs.reduce((docOptions, doc) => {
35
- if (loadedIDs.indexOf(doc.id) === -1) {
36
- loadedIDs.push(doc.id);
37
- return [
38
- ...docOptions,
39
- {
40
- label: doc[labelKey] || `${i18n.t('general:untitled')} - ID: ${doc.id}`,
41
- value: doc.id,
42
- },
43
- ];
44
- }
45
- return docOptions;
46
- }, []),
47
- ];
48
- ids.forEach((id) => {
49
- if (!loadedIDs.includes(id)) {
50
- options.push({
51
- label: labelKey === 'id' ? id : `${i18n.t('general:untitled')} - ID: ${id}`,
52
- value: id,
53
- });
54
- }
55
- });
56
- return sort ? sortOptions(options) : options;
57
- }
58
31
  const newOptions = [...state];
59
32
  const optionsToAddTo = newOptions.find((optionGroup) => optionGroup.label === collection.labels.plural);
60
33
  const newSubOptions = docs.reduce((docSubOptions, doc) => {
@@ -74,6 +47,7 @@ const optionsReducer = (state, action) => {
74
47
  ids.forEach((id) => {
75
48
  if (!loadedIDs.includes(id)) {
76
49
  newSubOptions.push({
50
+ relationTo: relation,
77
51
  label: labelKey === 'id' ? id : `${i18n.t('general:untitled')} - ID: ${id}`,
78
52
  value: id,
79
53
  });
@@ -90,7 +64,6 @@ const optionsReducer = (state, action) => {
90
64
  newOptions.push({
91
65
  label: getTranslation(collection.labels.plural, i18n),
92
66
  options: sort ? sortOptions(newSubOptions) : newSubOptions,
93
- value: undefined,
94
67
  });
95
68
  }
96
69
  return newOptions;
@@ -1,22 +1,30 @@
1
1
  import i18n from 'i18next';
2
2
  import { SanitizedCollectionConfig } from '../../../../../collections/config/types';
3
3
  import { RelationshipField } from '../../../../../fields/config/types';
4
+ import { Where } from '../../../../../types';
4
5
  export declare type Props = Omit<RelationshipField, 'type'> & {
5
6
  path?: string;
6
7
  };
7
8
  export declare type Option = {
8
9
  label: string;
9
- value: string;
10
+ value: string | number;
10
11
  relationTo?: string;
11
12
  options?: Option[];
12
13
  };
14
+ export declare type OptionGroup = {
15
+ label: string;
16
+ options: Option[];
17
+ };
18
+ export declare type Value = {
19
+ relationTo: string;
20
+ value: string | number;
21
+ } | string | number;
13
22
  declare type CLEAR = {
14
23
  type: 'CLEAR';
15
24
  };
16
25
  declare type ADD = {
17
26
  type: 'ADD';
18
27
  docs: any[];
19
- hasMultipleRelations: boolean;
20
28
  collection: SanitizedCollectionConfig;
21
29
  sort?: boolean;
22
30
  ids?: unknown[];
@@ -33,5 +41,9 @@ export declare type GetResults = (args: {
33
41
  search?: string;
34
42
  value?: unknown;
35
43
  sort?: boolean;
44
+ onSuccess?: () => void;
36
45
  }) => Promise<void>;
46
+ export declare type FilterOptionsResult = {
47
+ [relation: string]: Where;
48
+ };
37
49
  export {};
@@ -15,6 +15,7 @@ import { getBaseFields } from './Modal/baseFields';
15
15
  import reduceFieldsToValues from '../../../../Form/reduceFieldsToValues';
16
16
  import deepCopyObject from '../../../../../../../utilities/deepCopyObject';
17
17
  import Button from '../../../../../elements/Button';
18
+ import { getTranslation } from '../../../../../../../utilities/getTranslation';
18
19
  import './index.scss';
19
20
  const baseClass = 'rich-text-link';
20
21
  // TODO: Multiple modal windows stacked go boom (rip). Edit Upload in fields -> rich text
@@ -25,7 +26,7 @@ export const LinkElement = ({ attributes, children, element, editorRef, fieldPro
25
26
  const config = useConfig();
26
27
  const { user } = useAuth();
27
28
  const locale = useLocale();
28
- const { t } = useTranslation('fields');
29
+ const { t, i18n } = useTranslation('fields');
29
30
  const { openModal, toggleModal } = useModal();
30
31
  const [renderModal, setRenderModal] = useState(false);
31
32
  const [renderPopup, setRenderPopup] = useState(false);
@@ -100,8 +101,7 @@ export const LinkElement = ({ attributes, children, element, editorRef, fieldPro
100
101
  React.createElement(Popup, { buttonType: "none", size: "small", forceOpen: renderPopup, onToggleOpen: handleTogglePopup, horizontalAlign: "left", verticalAlign: "bottom", boundingRef: editorRef, render: () => {
101
102
  var _a, _b, _c, _d;
102
103
  return (React.createElement("div", { className: `${baseClass}__popup` },
103
- element.linkType === 'internal' && ((_a = element.doc) === null || _a === void 0 ? void 0 : _a.relationTo) && ((_b = element.doc) === null || _b === void 0 ? void 0 : _b.value) && (React.createElement(Trans, { i18nKey: "linkedTo", values: { label: (_d = (_c = config.collections.find(({ slug }) => slug === element.doc.relationTo)) === null || _c === void 0 ? void 0 : _c.labels) === null || _d === void 0 ? void 0 : _d.singular } },
104
- "linkedTo",
104
+ element.linkType === 'internal' && ((_a = element.doc) === null || _a === void 0 ? void 0 : _a.relationTo) && ((_b = element.doc) === null || _b === void 0 ? void 0 : _b.value) && (React.createElement(Trans, { i18nKey: "fields:linkedTo", values: { label: getTranslation((_d = (_c = config.collections.find(({ slug }) => slug === element.doc.relationTo)) === null || _c === void 0 ? void 0 : _c.labels) === null || _d === void 0 ? void 0 : _d.singular, i18n) } },
105
105
  React.createElement("a", { className: `${baseClass}__link-label`, href: `${config.routes.admin}/collections/${element.doc.relationTo}/${element.doc.value}`, target: "_blank", rel: "noreferrer" }, "label"))),
106
106
  (element.linkType === 'custom' || !element.linkType) && (React.createElement("a", { className: `${baseClass}__link-label`, href: element.url, target: "_blank", rel: "noreferrer" }, element.url)),
107
107
  React.createElement(Button, { className: `${baseClass}__link-edit`, icon: "edit", round: true, buttonStyle: "icon-label", onClick: (e) => {
@@ -18,6 +18,7 @@
18
18
  }
19
19
 
20
20
  .popup__scroll {
21
+ white-space: pre;
21
22
  padding-right: base(.5);
22
23
  }
23
24
  }
@@ -66,4 +67,4 @@
66
67
  &--open {
67
68
  z-index: var(--z-popup);
68
69
  }
69
- }
70
+ }
@@ -96,7 +96,7 @@ const UploadButton = ({ path }) => {
96
96
  React.createElement(UploadIcon, null)),
97
97
  renderModal && (React.createElement(Modal, { className: baseModalClass, slug: modalSlug }, isOpen && (React.createElement(MinimalTemplate, { width: "wide" },
98
98
  React.createElement("header", { className: `${baseModalClass}__header` },
99
- React.createElement("h1", null, t('addLabel', { label: getTranslation(modalCollection.labels.singular, i18n) })),
99
+ React.createElement("h1", null, t('fields:addLabel', { label: getTranslation(modalCollection.labels.singular, i18n) })),
100
100
  React.createElement(Button, { icon: "x", round: true, buttonStyle: "icon-label", iconStyle: "with-border", onClick: () => {
101
101
  toggleModal(modalSlug);
102
102
  setRenderModal(false);
@@ -4,7 +4,7 @@ import withCondition from '../../withCondition';
4
4
  import { getFieldPath } from '../getFieldPath';
5
5
  import './index.scss';
6
6
  const Row = (props) => {
7
- const { fields, fieldTypes, path, permissions, admin: { readOnly, className, }, indexPath } = props;
7
+ const { fields, fieldTypes, path, permissions, admin: { readOnly, className, }, indexPath, } = props;
8
8
  const classes = [
9
9
  'field-type',
10
10
  'row',
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { OptionObject, SelectField } from '../../../../../fields/config/types';
3
3
  import { Description } from '../../FieldDescription/types';
4
- import { Value as ReactSelectValue } from '../../../elements/ReactSelect/types';
4
+ import { Option } from '../../../elements/ReactSelect/types';
5
5
  import './index.scss';
6
6
  export declare type SelectInputProps = Omit<SelectField, 'type' | 'value' | 'options'> & {
7
7
  showError?: boolean;
@@ -11,7 +11,7 @@ export declare type SelectInputProps = Omit<SelectField, 'type' | 'value' | 'opt
11
11
  required?: boolean;
12
12
  value?: string | string[];
13
13
  description?: Description;
14
- onChange?: (value: ReactSelectValue) => void;
14
+ onChange?: (value: Option) => void;
15
15
  style?: React.CSSProperties;
16
16
  className?: string;
17
17
  width?: string;
@@ -1,3 +1,8 @@
1
1
  import { Options, FieldType } from './types';
2
+ /**
3
+ * Get and set the value of a form field.
4
+ *
5
+ * @see https://payloadcms.com/docs/admin/hooks#usefield
6
+ */
2
7
  declare const useField: <T extends unknown>(options: Options) => FieldType<T>;
3
8
  export default useField;
@@ -5,6 +5,11 @@ import { useFormProcessing, useFormSubmitted, useFormModified, useForm, useFormF
5
5
  import { useDocumentInfo } from '../../utilities/DocumentInfo';
6
6
  import { useOperation } from '../../utilities/OperationProvider';
7
7
  import useThrottledEffect from '../../../hooks/useThrottledEffect';
8
+ /**
9
+ * Get and set the value of a form field.
10
+ *
11
+ * @see https://payloadcms.com/docs/admin/hooks#usefield
12
+ */
8
13
  const useField = (options) => {
9
14
  const { path, validate, disableFormData = false, condition, } = options;
10
15
  const submitted = useFormSubmitted();
@@ -12,7 +12,7 @@ export const I18n = () => {
12
12
  i18n
13
13
  .use(LanguageDetector)
14
14
  .use(initReactI18next)
15
- .init(deepmerge(defaultOptions, config.i18n || { debug: true }));
15
+ .init(deepmerge(defaultOptions, config.i18n || {}));
16
16
  return null;
17
17
  };
18
18
  export default I18n;