@payloadcms/ui 4.0.0-canary.4 → 4.0.0-canary.5

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 (69) hide show
  1. package/dist/elements/ArrayAction/index.d.ts.map +1 -1
  2. package/dist/elements/ArrayAction/index.js +1 -0
  3. package/dist/elements/ArrayAction/index.js.map +1 -1
  4. package/dist/elements/Button/index.d.ts.map +1 -1
  5. package/dist/elements/Button/index.js +5 -0
  6. package/dist/elements/Button/index.js.map +1 -1
  7. package/dist/elements/ClipboardAction/index.d.ts.map +1 -1
  8. package/dist/elements/ClipboardAction/index.js +1 -0
  9. package/dist/elements/ClipboardAction/index.js.map +1 -1
  10. package/dist/elements/Collapsible/index.d.ts.map +1 -1
  11. package/dist/elements/Collapsible/index.js +2 -0
  12. package/dist/elements/Collapsible/index.js.map +1 -1
  13. package/dist/elements/DocumentControls/index.d.ts.map +1 -1
  14. package/dist/elements/DocumentControls/index.js +1 -0
  15. package/dist/elements/DocumentControls/index.js.map +1 -1
  16. package/dist/elements/Popup/PopupTrigger/index.d.ts +1 -0
  17. package/dist/elements/Popup/PopupTrigger/index.d.ts.map +1 -1
  18. package/dist/elements/Popup/PopupTrigger/index.js +3 -0
  19. package/dist/elements/Popup/PopupTrigger/index.js.map +1 -1
  20. package/dist/elements/Popup/index.d.ts +6 -0
  21. package/dist/elements/Popup/index.d.ts.map +1 -1
  22. package/dist/elements/Popup/index.js +2 -0
  23. package/dist/elements/Popup/index.js.map +1 -1
  24. package/dist/elements/ReactSelect/DropdownIndicator/index.d.ts.map +1 -1
  25. package/dist/elements/ReactSelect/DropdownIndicator/index.js +19 -12
  26. package/dist/elements/ReactSelect/DropdownIndicator/index.js.map +1 -1
  27. package/dist/elements/ReactSelect/types.d.ts +1 -0
  28. package/dist/elements/ReactSelect/types.d.ts.map +1 -1
  29. package/dist/elements/ReactSelect/types.js.map +1 -1
  30. package/dist/exports/client/index.d.ts +1 -0
  31. package/dist/exports/client/index.d.ts.map +1 -1
  32. package/dist/exports/client/index.js +13 -13
  33. package/dist/exports/client/index.js.map +4 -4
  34. package/dist/exports/rsc/index.d.ts +1 -0
  35. package/dist/exports/rsc/index.d.ts.map +1 -1
  36. package/dist/exports/rsc/index.js +1 -0
  37. package/dist/exports/rsc/index.js.map +1 -1
  38. package/dist/fields/Blocks/SectionTitle/index.d.ts.map +1 -1
  39. package/dist/fields/Blocks/SectionTitle/index.js +1 -0
  40. package/dist/fields/Blocks/SectionTitle/index.js.map +1 -1
  41. package/dist/fields/Relationship/Input.d.ts.map +1 -1
  42. package/dist/fields/Relationship/Input.js +2 -0
  43. package/dist/fields/Relationship/Input.js.map +1 -1
  44. package/dist/fields/Select/Input.d.ts.map +1 -1
  45. package/dist/fields/Select/Input.js +1 -0
  46. package/dist/fields/Select/Input.js.map +1 -1
  47. package/dist/views/Dashboard/Default/ModularDashboard/WidgetConfigDrawer.d.ts.map +1 -1
  48. package/dist/views/Dashboard/Default/ModularDashboard/WidgetConfigDrawer.js +27 -23
  49. package/dist/views/Dashboard/Default/ModularDashboard/WidgetConfigDrawer.js.map +1 -1
  50. package/dist/views/Dashboard/Default/ModularDashboard/index.css +2 -0
  51. package/dist/views/Version/SelectComparison/index.d.ts.map +1 -1
  52. package/dist/views/Version/SelectComparison/index.js +1 -0
  53. package/dist/views/Version/SelectComparison/index.js.map +1 -1
  54. package/dist/widgets/CollectionQuery/SortField/index.d.ts +3 -0
  55. package/dist/widgets/CollectionQuery/SortField/index.d.ts.map +1 -0
  56. package/dist/widgets/CollectionQuery/SortField/index.js +118 -0
  57. package/dist/widgets/CollectionQuery/SortField/index.js.map +1 -0
  58. package/dist/widgets/CollectionQuery/getCollectionFieldPaths.d.ts +18 -0
  59. package/dist/widgets/CollectionQuery/getCollectionFieldPaths.d.ts.map +1 -0
  60. package/dist/widgets/CollectionQuery/getCollectionFieldPaths.js +91 -0
  61. package/dist/widgets/CollectionQuery/getCollectionFieldPaths.js.map +1 -0
  62. package/dist/widgets/CollectionQuery/getCollectionFieldPaths.spec.js +100 -0
  63. package/dist/widgets/CollectionQuery/getCollectionFieldPaths.spec.js.map +1 -0
  64. package/dist/widgets/CollectionQuery/index.css +206 -0
  65. package/dist/widgets/CollectionQuery/index.d.ts +17 -0
  66. package/dist/widgets/CollectionQuery/index.d.ts.map +1 -0
  67. package/dist/widgets/CollectionQuery/index.js +399 -0
  68. package/dist/widgets/CollectionQuery/index.js.map +1 -0
  69. package/package.json +5 -5
@@ -14,6 +14,8 @@
14
14
 
15
15
  .widget-config-drawer__content {
16
16
  height: 100%;
17
+ padding-top: var(--gutter-v);
18
+ padding-bottom: var(--gutter-v);
17
19
 
18
20
  .form-submit {
19
21
  margin-top: var(--spacer-2-5);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/views/Version/SelectComparison/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAqC,MAAM,OAAO,CAAA;AAGzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAKvC,OAAO,aAAa,CAAA;AAKpB,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAoD3C,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/views/Version/SelectComparison/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAqC,MAAM,OAAO,CAAA;AAGzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAKvC,OAAO,aAAa,CAAA;AAKpB,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAqD3C,CAAA"}
@@ -48,6 +48,7 @@ export const SelectComparison = /*#__PURE__*/memo(props => {
48
48
  return /*#__PURE__*/_jsxs("div", {
49
49
  className: [fieldBaseClass, baseClass].filter(Boolean).join(' '),
50
50
  children: [/*#__PURE__*/_jsx(ReactSelect, {
51
+ "aria-label": t('version:selectVersionToCompare'),
51
52
  isClearable: false,
52
53
  isSearchable: false,
53
54
  onChange: onChange,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["React","memo","useCallback","useMemo","ReactSelect","fieldBaseClass","useTranslation","useVersionDrawer","baseClass","SelectComparison","props","collectionSlug","docID","globalSlug","onChange","onChangeFromProps","versionFromID","versionFromOptions","t","Drawer","openDrawer","options","label","_jsx","className","value","currentOption","find","option","val","_jsxs","filter","Boolean","join","isClearable","isSearchable","placeholder"],"sources":["../../../../src/views/Version/SelectComparison/index.tsx"],"sourcesContent":["'use client'\n\nimport React, { memo, useCallback, useMemo } from 'react'\n\nimport type { CompareOption } from '../Default/types.js'\nimport type { Props } from './types.js'\n\nimport { ReactSelect } from '../../../elements/ReactSelect/index.js'\nimport { fieldBaseClass } from '../../../fields/shared/index.js'\nimport { useTranslation } from '../../../providers/Translation/index.js'\nimport './index.css'\nimport { useVersionDrawer } from './VersionDrawer/index.js'\n\nconst baseClass = 'compare-version'\n\nexport const SelectComparison: React.FC<Props> = memo((props) => {\n const {\n collectionSlug,\n docID,\n globalSlug,\n onChange: onChangeFromProps,\n versionFromID,\n versionFromOptions,\n } = props\n const { t } = useTranslation()\n\n const { Drawer, openDrawer } = useVersionDrawer({ collectionSlug, docID, globalSlug })\n\n const options = useMemo(() => {\n return [\n ...versionFromOptions,\n {\n label: <span className={`${baseClass}-moreVersions`}>{t('version:moreVersions')}</span>,\n value: 'more',\n },\n ]\n }, [t, versionFromOptions])\n\n const currentOption = useMemo(\n () => versionFromOptions.find((option) => option.value === versionFromID),\n [versionFromOptions, versionFromID],\n )\n\n const onChange = useCallback(\n (val: CompareOption) => {\n if (val.value === 'more') {\n openDrawer()\n return\n }\n onChangeFromProps(val)\n },\n [onChangeFromProps, openDrawer],\n )\n\n return (\n <div className={[fieldBaseClass, baseClass].filter(Boolean).join(' ')}>\n <ReactSelect\n isClearable={false}\n isSearchable={false}\n onChange={onChange}\n options={options}\n placeholder={t('version:selectVersionToCompare')}\n value={currentOption}\n />\n <Drawer />\n </div>\n )\n})\n"],"mappings":"AAAA;;;AAEA,OAAOA,KAAA,IAASC,IAAI,EAAEC,WAAW,EAAEC,OAAO,QAAQ;AAKlD,SAASC,WAAW,QAAQ;AAC5B,SAASC,cAAc,QAAQ;AAC/B,SAASC,cAAc,QAAQ;AAC/B,OAAO;AACP,SAASC,gBAAgB,QAAQ;AAEjC,MAAMC,SAAA,GAAY;AAElB,OAAO,MAAMC,gBAAA,gBAAoCR,IAAA,CAAMS,KAAA;EACrD,MAAM;IACJC,cAAc;IACdC,KAAK;IACLC,UAAU;IACVC,QAAA,EAAUC,iBAAiB;IAC3BC,aAAa;IACbC;EAAkB,CACnB,GAAGP,KAAA;EACJ,MAAM;IAAEQ;EAAC,CAAE,GAAGZ,cAAA;EAEd,MAAM;IAAEa,MAAM;IAAEC;EAAU,CAAE,GAAGb,gBAAA,CAAiB;IAAEI,cAAA;IAAgBC,KAAA;IAAOC;EAAW;EAEpF,MAAMQ,OAAA,GAAUlB,OAAA,CAAQ;IACtB,OAAO,C,GACFc,kBAAA,EACH;MACEK,KAAA,eAAOC,IAAA,CAAC;QAAKC,SAAA,EAAW,GAAGhB,SAAA,eAAwB;kBAAGU,CAAA,CAAE;;MACxDO,KAAA,EAAO;IACT,EACD;EACH,GAAG,CAACP,CAAA,EAAGD,kBAAA,CAAmB;EAE1B,MAAMS,aAAA,GAAgBvB,OAAA,CACpB,MAAMc,kBAAA,CAAmBU,IAAI,CAAEC,MAAA,IAAWA,MAAA,CAAOH,KAAK,KAAKT,aAAA,GAC3D,CAACC,kBAAA,EAAoBD,aAAA,CAAc;EAGrC,MAAMF,QAAA,GAAWZ,WAAA,CACd2B,GAAA;IACC,IAAIA,GAAA,CAAIJ,KAAK,KAAK,QAAQ;MACxBL,UAAA;MACA;IACF;IACAL,iBAAA,CAAkBc,GAAA;EACpB,GACA,CAACd,iBAAA,EAAmBK,UAAA,CAAW;EAGjC,oBACEU,KAAA,CAAC;IAAIN,SAAA,EAAW,CAACnB,cAAA,EAAgBG,SAAA,CAAU,CAACuB,MAAM,CAACC,OAAA,EAASC,IAAI,CAAC;4BAC/DV,IAAA,CAACnB,WAAA;MACC8B,WAAA,EAAa;MACbC,YAAA,EAAc;MACdrB,QAAA,EAAUA,QAAA;MACVO,OAAA,EAASA,OAAA;MACTe,WAAA,EAAalB,CAAA,CAAE;MACfO,KAAA,EAAOC;qBAETH,IAAA,CAACJ,MAAA;;AAGP","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["React","memo","useCallback","useMemo","ReactSelect","fieldBaseClass","useTranslation","useVersionDrawer","baseClass","SelectComparison","props","collectionSlug","docID","globalSlug","onChange","onChangeFromProps","versionFromID","versionFromOptions","t","Drawer","openDrawer","options","label","_jsx","className","value","currentOption","find","option","val","_jsxs","filter","Boolean","join","isClearable","isSearchable","placeholder"],"sources":["../../../../src/views/Version/SelectComparison/index.tsx"],"sourcesContent":["'use client'\n\nimport React, { memo, useCallback, useMemo } from 'react'\n\nimport type { CompareOption } from '../Default/types.js'\nimport type { Props } from './types.js'\n\nimport { ReactSelect } from '../../../elements/ReactSelect/index.js'\nimport { fieldBaseClass } from '../../../fields/shared/index.js'\nimport { useTranslation } from '../../../providers/Translation/index.js'\nimport './index.css'\nimport { useVersionDrawer } from './VersionDrawer/index.js'\n\nconst baseClass = 'compare-version'\n\nexport const SelectComparison: React.FC<Props> = memo((props) => {\n const {\n collectionSlug,\n docID,\n globalSlug,\n onChange: onChangeFromProps,\n versionFromID,\n versionFromOptions,\n } = props\n const { t } = useTranslation()\n\n const { Drawer, openDrawer } = useVersionDrawer({ collectionSlug, docID, globalSlug })\n\n const options = useMemo(() => {\n return [\n ...versionFromOptions,\n {\n label: <span className={`${baseClass}-moreVersions`}>{t('version:moreVersions')}</span>,\n value: 'more',\n },\n ]\n }, [t, versionFromOptions])\n\n const currentOption = useMemo(\n () => versionFromOptions.find((option) => option.value === versionFromID),\n [versionFromOptions, versionFromID],\n )\n\n const onChange = useCallback(\n (val: CompareOption) => {\n if (val.value === 'more') {\n openDrawer()\n return\n }\n onChangeFromProps(val)\n },\n [onChangeFromProps, openDrawer],\n )\n\n return (\n <div className={[fieldBaseClass, baseClass].filter(Boolean).join(' ')}>\n <ReactSelect\n aria-label={t('version:selectVersionToCompare')}\n isClearable={false}\n isSearchable={false}\n onChange={onChange}\n options={options}\n placeholder={t('version:selectVersionToCompare')}\n value={currentOption}\n />\n <Drawer />\n </div>\n )\n})\n"],"mappings":"AAAA;;;AAEA,OAAOA,KAAA,IAASC,IAAI,EAAEC,WAAW,EAAEC,OAAO,QAAQ;AAKlD,SAASC,WAAW,QAAQ;AAC5B,SAASC,cAAc,QAAQ;AAC/B,SAASC,cAAc,QAAQ;AAC/B,OAAO;AACP,SAASC,gBAAgB,QAAQ;AAEjC,MAAMC,SAAA,GAAY;AAElB,OAAO,MAAMC,gBAAA,gBAAoCR,IAAA,CAAMS,KAAA;EACrD,MAAM;IACJC,cAAc;IACdC,KAAK;IACLC,UAAU;IACVC,QAAA,EAAUC,iBAAiB;IAC3BC,aAAa;IACbC;EAAkB,CACnB,GAAGP,KAAA;EACJ,MAAM;IAAEQ;EAAC,CAAE,GAAGZ,cAAA;EAEd,MAAM;IAAEa,MAAM;IAAEC;EAAU,CAAE,GAAGb,gBAAA,CAAiB;IAAEI,cAAA;IAAgBC,KAAA;IAAOC;EAAW;EAEpF,MAAMQ,OAAA,GAAUlB,OAAA,CAAQ;IACtB,OAAO,C,GACFc,kBAAA,EACH;MACEK,KAAA,eAAOC,IAAA,CAAC;QAAKC,SAAA,EAAW,GAAGhB,SAAA,eAAwB;kBAAGU,CAAA,CAAE;;MACxDO,KAAA,EAAO;IACT,EACD;EACH,GAAG,CAACP,CAAA,EAAGD,kBAAA,CAAmB;EAE1B,MAAMS,aAAA,GAAgBvB,OAAA,CACpB,MAAMc,kBAAA,CAAmBU,IAAI,CAAEC,MAAA,IAAWA,MAAA,CAAOH,KAAK,KAAKT,aAAA,GAC3D,CAACC,kBAAA,EAAoBD,aAAA,CAAc;EAGrC,MAAMF,QAAA,GAAWZ,WAAA,CACd2B,GAAA;IACC,IAAIA,GAAA,CAAIJ,KAAK,KAAK,QAAQ;MACxBL,UAAA;MACA;IACF;IACAL,iBAAA,CAAkBc,GAAA;EACpB,GACA,CAACd,iBAAA,EAAmBK,UAAA,CAAW;EAGjC,oBACEU,KAAA,CAAC;IAAIN,SAAA,EAAW,CAACnB,cAAA,EAAgBG,SAAA,CAAU,CAACuB,MAAM,CAACC,OAAA,EAASC,IAAI,CAAC;4BAC/DV,IAAA,CAACnB,WAAA;MACC,cAAYc,CAAA,CAAE;MACdgB,WAAA,EAAa;MACbC,YAAA,EAAc;MACdrB,QAAA,EAAUA,QAAA;MACVO,OAAA,EAASA,OAAA;MACTe,WAAA,EAAalB,CAAA,CAAE;MACfO,KAAA,EAAOC;qBAETH,IAAA,CAACJ,MAAA;;AAGP","ignoreList":[]}
@@ -0,0 +1,3 @@
1
+ import type { TextFieldClientComponent } from 'payload';
2
+ export declare const CollectionQuerySortField: TextFieldClientComponent;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/widgets/CollectionQuery/SortField/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAcrE,eAAO,MAAM,wBAAwB,EAAE,wBA6FtC,CAAA"}
@@ -0,0 +1,118 @@
1
+ 'use client';
2
+
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ import React, { useCallback, useMemo } from 'react';
5
+ import { SelectInput } from '../../../fields/Select/Input.js';
6
+ import { useField } from '../../../forms/useField/index.js';
7
+ import { useAuth } from '../../../providers/Auth/index.js';
8
+ import { useConfig } from '../../../providers/Config/index.js';
9
+ import { useTranslation } from '../../../providers/Translation/index.js';
10
+ import { reduceFieldsToOptions } from '../../../utilities/reduceFieldsToOptions.js';
11
+ import { getCollectionFieldPaths } from '../getCollectionFieldPaths.js';
12
+ const baseFieldValues = new Set(['createdAt', 'id', 'updatedAt']);
13
+ export const CollectionQuerySortField = ({
14
+ field,
15
+ path: pathFromProps,
16
+ readOnly
17
+ }) => {
18
+ const {
19
+ name,
20
+ admin,
21
+ label,
22
+ required
23
+ } = field;
24
+ const {
25
+ customComponents: {
26
+ AfterInput,
27
+ BeforeInput,
28
+ Description,
29
+ Error,
30
+ Label
31
+ } = {},
32
+ path,
33
+ setValue,
34
+ showError,
35
+ value
36
+ } = useField({
37
+ potentiallyStalePath: pathFromProps
38
+ });
39
+ const relatedCollectionField = useField({
40
+ path: 'relatedCollection'
41
+ });
42
+ const relatedCollection = relatedCollectionField.value;
43
+ const {
44
+ config
45
+ } = useConfig();
46
+ const {
47
+ permissions
48
+ } = useAuth();
49
+ const {
50
+ i18n,
51
+ t
52
+ } = useTranslation();
53
+ const collectionConfig = useMemo(() => {
54
+ if (!relatedCollection) {
55
+ return null;
56
+ }
57
+ return config.collections?.find(collection => collection.slug === relatedCollection) ?? null;
58
+ }, [config.collections, relatedCollection]);
59
+ const options = useMemo(() => {
60
+ if (!relatedCollection || !collectionConfig) {
61
+ return [];
62
+ }
63
+ // Reuse the same sortable-path rules as the server widget so the dropdown only offers fields
64
+ // the API can actually sort by (e.g. excluding array sub-fields).
65
+ const {
66
+ sortableFieldPaths
67
+ } = getCollectionFieldPaths(collectionConfig.fields);
68
+ const fieldOptions = reduceFieldsToOptions({
69
+ fieldPermissions: permissions?.collections?.[relatedCollection]?.fields ?? true,
70
+ fields: collectionConfig.fields,
71
+ i18n
72
+ }).filter(({
73
+ fieldPath
74
+ }) => sortableFieldPaths.has(fieldPath) && !baseFieldValues.has(fieldPath)).map(({
75
+ fieldPath: fieldPath_0,
76
+ plainTextLabel
77
+ }) => ({
78
+ label: plainTextLabel ?? fieldPath_0,
79
+ value: fieldPath_0
80
+ }));
81
+ return [{
82
+ label: 'ID',
83
+ value: 'id'
84
+ }, {
85
+ label: t('general:createdAt'),
86
+ value: 'createdAt'
87
+ }, {
88
+ label: t('general:updatedAt'),
89
+ value: 'updatedAt'
90
+ }, ...fieldOptions];
91
+ }, [collectionConfig, i18n, permissions, relatedCollection, t]);
92
+ const onChange = useCallback(selectedOption => {
93
+ if (Array.isArray(selectedOption)) {
94
+ return;
95
+ }
96
+ setValue(typeof selectedOption?.value === 'string' ? selectedOption.value : null);
97
+ }, [setValue]);
98
+ return /*#__PURE__*/_jsx(SelectInput, {
99
+ AfterInput: AfterInput,
100
+ BeforeInput: BeforeInput,
101
+ Description: Description,
102
+ description: admin?.description,
103
+ Error: Error,
104
+ isClearable: true,
105
+ Label: Label,
106
+ label: label,
107
+ name: name,
108
+ onChange: onChange,
109
+ options: options,
110
+ path: path,
111
+ placeholder: relatedCollection ? t('dashboard:widgetSelectSortField') : t('dashboard:widgetSelectCollectionFirst'),
112
+ readOnly: readOnly || !relatedCollection,
113
+ required: required,
114
+ showError: showError,
115
+ value: value
116
+ });
117
+ };
118
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["React","useCallback","useMemo","SelectInput","useField","useAuth","useConfig","useTranslation","reduceFieldsToOptions","getCollectionFieldPaths","baseFieldValues","Set","CollectionQuerySortField","field","path","pathFromProps","readOnly","name","admin","label","required","customComponents","AfterInput","BeforeInput","Description","Error","Label","setValue","showError","value","potentiallyStalePath","relatedCollectionField","relatedCollection","config","permissions","i18n","t","collectionConfig","collections","find","collection","slug","options","sortableFieldPaths","fields","fieldOptions","fieldPermissions","filter","fieldPath","has","map","plainTextLabel","onChange","selectedOption","Array","isArray","_jsx","description","isClearable","placeholder"],"sources":["../../../../src/widgets/CollectionQuery/SortField/index.tsx"],"sourcesContent":["'use client'\nimport type { OptionObject, TextFieldClientComponent } from 'payload'\n\nimport React, { useCallback, useMemo } from 'react'\n\nimport { SelectInput } from '../../../fields/Select/Input.js'\nimport { useField } from '../../../forms/useField/index.js'\nimport { useAuth } from '../../../providers/Auth/index.js'\nimport { useConfig } from '../../../providers/Config/index.js'\nimport { useTranslation } from '../../../providers/Translation/index.js'\nimport { reduceFieldsToOptions } from '../../../utilities/reduceFieldsToOptions.js'\nimport { getCollectionFieldPaths } from '../getCollectionFieldPaths.js'\n\nconst baseFieldValues = new Set(['createdAt', 'id', 'updatedAt'])\n\nexport const CollectionQuerySortField: TextFieldClientComponent = ({\n field,\n path: pathFromProps,\n readOnly,\n}) => {\n const { name, admin, label, required } = field\n const {\n customComponents: { AfterInput, BeforeInput, Description, Error, Label } = {},\n path,\n setValue,\n showError,\n value,\n } = useField<string>({ potentiallyStalePath: pathFromProps })\n const relatedCollectionField = useField<string>({ path: 'relatedCollection' })\n const relatedCollection = relatedCollectionField.value\n const { config } = useConfig()\n const { permissions } = useAuth()\n const { i18n, t } = useTranslation()\n\n const collectionConfig = useMemo(() => {\n if (!relatedCollection) {\n return null\n }\n\n return config.collections?.find((collection) => collection.slug === relatedCollection) ?? null\n }, [config.collections, relatedCollection])\n\n const options = useMemo<OptionObject[]>(() => {\n if (!relatedCollection || !collectionConfig) {\n return []\n }\n\n // Reuse the same sortable-path rules as the server widget so the dropdown only offers fields\n // the API can actually sort by (e.g. excluding array sub-fields).\n const { sortableFieldPaths } = getCollectionFieldPaths(collectionConfig.fields)\n\n const fieldOptions = reduceFieldsToOptions({\n fieldPermissions: permissions?.collections?.[relatedCollection]?.fields ?? true,\n fields: collectionConfig.fields,\n i18n,\n })\n .filter(\n ({ fieldPath }) => sortableFieldPaths.has(fieldPath) && !baseFieldValues.has(fieldPath),\n )\n .map(({ fieldPath, plainTextLabel }) => ({\n label: plainTextLabel ?? fieldPath,\n value: fieldPath,\n }))\n\n return [\n { label: 'ID', value: 'id' },\n { label: t('general:createdAt'), value: 'createdAt' },\n { label: t('general:updatedAt'), value: 'updatedAt' },\n ...fieldOptions,\n ]\n }, [collectionConfig, i18n, permissions, relatedCollection, t])\n\n const onChange = useCallback(\n (selectedOption: OptionObject | OptionObject[]) => {\n if (Array.isArray(selectedOption)) {\n return\n }\n\n setValue(typeof selectedOption?.value === 'string' ? selectedOption.value : null)\n },\n [setValue],\n )\n\n return (\n <SelectInput\n AfterInput={AfterInput}\n BeforeInput={BeforeInput}\n Description={Description}\n description={admin?.description}\n Error={Error}\n isClearable\n Label={Label}\n label={label}\n name={name}\n onChange={onChange}\n options={options}\n path={path}\n placeholder={\n relatedCollection\n ? t('dashboard:widgetSelectSortField')\n : t('dashboard:widgetSelectCollectionFirst')\n }\n readOnly={readOnly || !relatedCollection}\n required={required}\n showError={showError}\n value={value}\n />\n )\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,18 @@
1
+ import type { ClientField, Field } from 'payload';
2
+ type QueryableField = ClientField | Field;
3
+ export type CollectionFieldPaths = {
4
+ /** Field paths that can be referenced in a `where` filter. */
5
+ filterableFieldPaths: Set<string>;
6
+ /** Field paths that point to another collection (relationship/upload/join). */
7
+ relationshipFieldPaths: Set<string>;
8
+ /** Field paths that the API can sort by. */
9
+ sortableFieldPaths: Set<string>;
10
+ };
11
+ /**
12
+ * Builds the queryable field paths for a collection from either server or client field schemas.
13
+ * Sortability stays anchored on Payload's `sortableFieldTypes` so the widget follows core rules as
14
+ * field types evolve, while array sub-fields remain filter-only.
15
+ */
16
+ export declare function getCollectionFieldPaths(fields: QueryableField[]): CollectionFieldPaths;
17
+ export {};
18
+ //# sourceMappingURL=getCollectionFieldPaths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getCollectionFieldPaths.d.ts","sourceRoot":"","sources":["../../../src/widgets/CollectionQuery/getCollectionFieldPaths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAIjD,KAAK,cAAc,GAAG,WAAW,GAAG,KAAK,CAAA;AAMzC,MAAM,MAAM,oBAAoB,GAAG;IACjC,8DAA8D;IAC9D,oBAAoB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACjC,+EAA+E;IAC/E,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACnC,4CAA4C;IAC5C,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAChC,CAAA;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,oBAAoB,CAUtF"}
@@ -0,0 +1,91 @@
1
+ import { fieldAffectsData, sortableFieldTypes } from 'payload/shared';
2
+ const baseFieldPaths = ['createdAt', 'id', 'updatedAt'];
3
+ const sortableFieldTypeSet = new Set(sortableFieldTypes);
4
+ const relationshipFieldTypes = new Set(['join', 'relationship', 'upload']);
5
+ /**
6
+ * Builds the queryable field paths for a collection from either server or client field schemas.
7
+ * Sortability stays anchored on Payload's `sortableFieldTypes` so the widget follows core rules as
8
+ * field types evolve, while array sub-fields remain filter-only.
9
+ */
10
+ export function getCollectionFieldPaths(fields) {
11
+ const paths = {
12
+ filterableFieldPaths: new Set(baseFieldPaths),
13
+ relationshipFieldPaths: new Set(),
14
+ sortableFieldPaths: new Set(baseFieldPaths)
15
+ };
16
+ addFieldPaths({
17
+ fields,
18
+ paths
19
+ });
20
+ return paths;
21
+ }
22
+ function addFieldPaths({
23
+ canSort = true,
24
+ fields,
25
+ parentPath,
26
+ paths
27
+ }) {
28
+ for (const field of fields) {
29
+ if ('virtual' in field && field.virtual === true) {
30
+ continue;
31
+ }
32
+ if (field.type === 'tabs' && 'tabs' in field) {
33
+ for (const tab of field.tabs) {
34
+ const path = 'name' in tab && tab.name ? joinPath(parentPath, tab.name) : parentPath;
35
+ addFieldPaths({
36
+ canSort,
37
+ fields: tab.fields,
38
+ parentPath: path,
39
+ paths
40
+ });
41
+ }
42
+ continue;
43
+ }
44
+ if ((field.type === 'collapsible' || field.type === 'row') && 'fields' in field) {
45
+ addFieldPaths({
46
+ canSort,
47
+ fields: field.fields,
48
+ parentPath,
49
+ paths
50
+ });
51
+ continue;
52
+ }
53
+ // Groups are queryable by their child paths. Named groups prefix those paths.
54
+ if (field.type === 'group' && 'fields' in field) {
55
+ const path = fieldAffectsData(field) ? joinPath(parentPath, field.name) : parentPath;
56
+ addFieldPaths({
57
+ canSort,
58
+ fields: field.fields,
59
+ parentPath: path,
60
+ paths
61
+ });
62
+ continue;
63
+ }
64
+ // Array sub-fields can be filtered on, but the API cannot sort by them.
65
+ if (field.type === 'array' && 'fields' in field) {
66
+ addFieldPaths({
67
+ canSort: false,
68
+ fields: field.fields,
69
+ parentPath: joinPath(parentPath, field.name),
70
+ paths
71
+ });
72
+ continue;
73
+ }
74
+ if (field.type === 'blocks' || !fieldAffectsData(field)) {
75
+ continue;
76
+ }
77
+ const name = 'virtual' in field && typeof field.virtual === 'string' ? field.virtual : field.name;
78
+ const path = joinPath(parentPath, name);
79
+ paths.filterableFieldPaths.add(path);
80
+ if (relationshipFieldTypes.has(field.type)) {
81
+ paths.relationshipFieldPaths.add(path);
82
+ }
83
+ if (canSort && sortableFieldTypeSet.has(field.type)) {
84
+ paths.sortableFieldPaths.add(path);
85
+ }
86
+ }
87
+ }
88
+ function joinPath(parentPath, path) {
89
+ return parentPath ? `${parentPath}.${path}` : path;
90
+ }
91
+ //# sourceMappingURL=getCollectionFieldPaths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getCollectionFieldPaths.js","names":["fieldAffectsData","sortableFieldTypes","baseFieldPaths","sortableFieldTypeSet","Set","relationshipFieldTypes","getCollectionFieldPaths","fields","paths","filterableFieldPaths","relationshipFieldPaths","sortableFieldPaths","addFieldPaths","canSort","parentPath","field","virtual","type","tab","tabs","path","name","joinPath","add","has"],"sources":["../../../src/widgets/CollectionQuery/getCollectionFieldPaths.ts"],"sourcesContent":["import type { ClientField, Field } from 'payload'\n\nimport { fieldAffectsData, sortableFieldTypes } from 'payload/shared'\n\ntype QueryableField = ClientField | Field\n\nconst baseFieldPaths = ['createdAt', 'id', 'updatedAt']\nconst sortableFieldTypeSet = new Set<string>(sortableFieldTypes)\nconst relationshipFieldTypes = new Set<string>(['join', 'relationship', 'upload'])\n\nexport type CollectionFieldPaths = {\n /** Field paths that can be referenced in a `where` filter. */\n filterableFieldPaths: Set<string>\n /** Field paths that point to another collection (relationship/upload/join). */\n relationshipFieldPaths: Set<string>\n /** Field paths that the API can sort by. */\n sortableFieldPaths: Set<string>\n}\n\n/**\n * Builds the queryable field paths for a collection from either server or client field schemas.\n * Sortability stays anchored on Payload's `sortableFieldTypes` so the widget follows core rules as\n * field types evolve, while array sub-fields remain filter-only.\n */\nexport function getCollectionFieldPaths(fields: QueryableField[]): CollectionFieldPaths {\n const paths: CollectionFieldPaths = {\n filterableFieldPaths: new Set(baseFieldPaths),\n relationshipFieldPaths: new Set(),\n sortableFieldPaths: new Set(baseFieldPaths),\n }\n\n addFieldPaths({ fields, paths })\n\n return paths\n}\n\nfunction addFieldPaths({\n canSort = true,\n fields,\n parentPath,\n paths,\n}: {\n canSort?: boolean\n fields: QueryableField[]\n parentPath?: string\n paths: CollectionFieldPaths\n}) {\n for (const field of fields) {\n if ('virtual' in field && field.virtual === true) {\n continue\n }\n\n if (field.type === 'tabs' && 'tabs' in field) {\n for (const tab of field.tabs) {\n const path = 'name' in tab && tab.name ? joinPath(parentPath, tab.name) : parentPath\n\n addFieldPaths({ canSort, fields: tab.fields, parentPath: path, paths })\n }\n\n continue\n }\n\n if ((field.type === 'collapsible' || field.type === 'row') && 'fields' in field) {\n addFieldPaths({ canSort, fields: field.fields, parentPath, paths })\n\n continue\n }\n\n // Groups are queryable by their child paths. Named groups prefix those paths.\n if (field.type === 'group' && 'fields' in field) {\n const path = fieldAffectsData(field) ? joinPath(parentPath, field.name) : parentPath\n\n addFieldPaths({ canSort, fields: field.fields, parentPath: path, paths })\n\n continue\n }\n\n // Array sub-fields can be filtered on, but the API cannot sort by them.\n if (field.type === 'array' && 'fields' in field) {\n addFieldPaths({\n canSort: false,\n fields: field.fields,\n parentPath: joinPath(parentPath, field.name),\n paths,\n })\n\n continue\n }\n\n if (field.type === 'blocks' || !fieldAffectsData(field)) {\n continue\n }\n\n const name =\n 'virtual' in field && typeof field.virtual === 'string' ? field.virtual : field.name\n const path = joinPath(parentPath, name)\n\n paths.filterableFieldPaths.add(path)\n\n if (relationshipFieldTypes.has(field.type)) {\n paths.relationshipFieldPaths.add(path)\n }\n\n if (canSort && sortableFieldTypeSet.has(field.type)) {\n paths.sortableFieldPaths.add(path)\n }\n }\n}\n\nfunction joinPath(parentPath: string | undefined, path: string) {\n return parentPath ? `${parentPath}.${path}` : path\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,100 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getCollectionFieldPaths } from './getCollectionFieldPaths.js';
3
+ const fields = [{
4
+ name: 'title',
5
+ type: 'text'
6
+ }, {
7
+ name: 'status',
8
+ type: 'select',
9
+ options: [{
10
+ label: 'Draft',
11
+ value: 'draft'
12
+ }, {
13
+ label: 'Published',
14
+ value: 'published'
15
+ }]
16
+ }, {
17
+ name: 'isActive',
18
+ type: 'checkbox'
19
+ }, {
20
+ name: 'category',
21
+ type: 'relationship',
22
+ relationTo: 'categories'
23
+ }, {
24
+ name: 'meta',
25
+ type: 'group',
26
+ fields: [{
27
+ name: 'subtitle',
28
+ type: 'text'
29
+ }]
30
+ }, {
31
+ name: 'items',
32
+ type: 'array',
33
+ fields: [{
34
+ name: 'label',
35
+ type: 'text'
36
+ }]
37
+ }, {
38
+ type: 'tabs',
39
+ tabs: [{
40
+ name: 'seo',
41
+ fields: [{
42
+ name: 'keyword',
43
+ type: 'text'
44
+ }]
45
+ }, {
46
+ fields: [{
47
+ name: 'flat',
48
+ type: 'text'
49
+ }],
50
+ label: 'Layout'
51
+ }]
52
+ }, {
53
+ type: 'row',
54
+ fields: [{
55
+ name: 'rowField',
56
+ type: 'text'
57
+ }]
58
+ }];
59
+ describe('getCollectionFieldPaths', () => {
60
+ const {
61
+ filterableFieldPaths,
62
+ relationshipFieldPaths,
63
+ sortableFieldPaths
64
+ } = getCollectionFieldPaths(fields);
65
+ it('should always include base document fields', () => {
66
+ for (const path of ['createdAt', 'id', 'updatedAt']) {
67
+ expect(sortableFieldPaths.has(path)).toBe(true);
68
+ expect(filterableFieldPaths.has(path)).toBe(true);
69
+ }
70
+ });
71
+ it('should mark top-level sortable field types as sortable', () => {
72
+ expect(sortableFieldPaths.has('title')).toBe(true);
73
+ expect(sortableFieldPaths.has('status')).toBe(true);
74
+ });
75
+ it('should not mark non-sortable field types as sortable but keep them filterable', () => {
76
+ expect(sortableFieldPaths.has('isActive')).toBe(false);
77
+ expect(filterableFieldPaths.has('isActive')).toBe(true);
78
+ });
79
+ it('should treat relationships as filterable, flagged, and not sortable', () => {
80
+ expect(filterableFieldPaths.has('category')).toBe(true);
81
+ expect(relationshipFieldPaths.has('category')).toBe(true);
82
+ expect(sortableFieldPaths.has('category')).toBe(false);
83
+ });
84
+ it('should flatten group sub-fields into sortable and filterable paths', () => {
85
+ expect(sortableFieldPaths.has('meta.subtitle')).toBe(true);
86
+ expect(filterableFieldPaths.has('meta.subtitle')).toBe(true);
87
+ });
88
+ it('should keep array sub-fields filterable but never sortable', () => {
89
+ expect(filterableFieldPaths.has('items.label')).toBe(true);
90
+ expect(sortableFieldPaths.has('items.label')).toBe(false);
91
+ });
92
+ it('should prefix named tab sub-fields and inline unnamed tab sub-fields', () => {
93
+ expect(sortableFieldPaths.has('seo.keyword')).toBe(true);
94
+ expect(sortableFieldPaths.has('flat')).toBe(true);
95
+ });
96
+ it('should inline presentational row sub-fields', () => {
97
+ expect(sortableFieldPaths.has('rowField')).toBe(true);
98
+ });
99
+ });
100
+ //# sourceMappingURL=getCollectionFieldPaths.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getCollectionFieldPaths.spec.js","names":["describe","expect","it","getCollectionFieldPaths","fields","name","type","options","label","value","relationTo","tabs","filterableFieldPaths","relationshipFieldPaths","sortableFieldPaths","path","has","toBe"],"sources":["../../../src/widgets/CollectionQuery/getCollectionFieldPaths.spec.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nimport { describe, expect, it } from 'vitest'\n\nimport { getCollectionFieldPaths } from './getCollectionFieldPaths.js'\n\nconst fields: Field[] = [\n { name: 'title', type: 'text' },\n {\n name: 'status',\n type: 'select',\n options: [\n { label: 'Draft', value: 'draft' },\n { label: 'Published', value: 'published' },\n ],\n },\n { name: 'isActive', type: 'checkbox' },\n { name: 'category', type: 'relationship', relationTo: 'categories' },\n {\n name: 'meta',\n type: 'group',\n fields: [{ name: 'subtitle', type: 'text' }],\n },\n {\n name: 'items',\n type: 'array',\n fields: [{ name: 'label', type: 'text' }],\n },\n {\n type: 'tabs',\n tabs: [\n { name: 'seo', fields: [{ name: 'keyword', type: 'text' }] },\n { fields: [{ name: 'flat', type: 'text' }], label: 'Layout' },\n ],\n },\n {\n type: 'row',\n fields: [{ name: 'rowField', type: 'text' }],\n },\n]\n\ndescribe('getCollectionFieldPaths', () => {\n const { filterableFieldPaths, relationshipFieldPaths, sortableFieldPaths } =\n getCollectionFieldPaths(fields)\n\n it('should always include base document fields', () => {\n for (const path of ['createdAt', 'id', 'updatedAt']) {\n expect(sortableFieldPaths.has(path)).toBe(true)\n expect(filterableFieldPaths.has(path)).toBe(true)\n }\n })\n\n it('should mark top-level sortable field types as sortable', () => {\n expect(sortableFieldPaths.has('title')).toBe(true)\n expect(sortableFieldPaths.has('status')).toBe(true)\n })\n\n it('should not mark non-sortable field types as sortable but keep them filterable', () => {\n expect(sortableFieldPaths.has('isActive')).toBe(false)\n expect(filterableFieldPaths.has('isActive')).toBe(true)\n })\n\n it('should treat relationships as filterable, flagged, and not sortable', () => {\n expect(filterableFieldPaths.has('category')).toBe(true)\n expect(relationshipFieldPaths.has('category')).toBe(true)\n expect(sortableFieldPaths.has('category')).toBe(false)\n })\n\n it('should flatten group sub-fields into sortable and filterable paths', () => {\n expect(sortableFieldPaths.has('meta.subtitle')).toBe(true)\n expect(filterableFieldPaths.has('meta.subtitle')).toBe(true)\n })\n\n it('should keep array sub-fields filterable but never sortable', () => {\n expect(filterableFieldPaths.has('items.label')).toBe(true)\n expect(sortableFieldPaths.has('items.label')).toBe(false)\n })\n\n it('should prefix named tab sub-fields and inline unnamed tab sub-fields', () => {\n expect(sortableFieldPaths.has('seo.keyword')).toBe(true)\n expect(sortableFieldPaths.has('flat')).toBe(true)\n })\n\n it('should inline presentational row sub-fields', () => {\n expect(sortableFieldPaths.has('rowField')).toBe(true)\n })\n})\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,206 @@
1
+ @layer payload-default {
2
+ .collection-query-widget {
3
+ --collection-query-widget-visible-rows: 5;
4
+ --collection-query-widget-row-gap: var(--spacer-1);
5
+ --collection-query-widget-row-height: 40px;
6
+ --collection-query-widget-scrollbar-width: 6px;
7
+ --collection-query-widget-rows-height: calc(
8
+ (
9
+ var(--collection-query-widget-row-height) + var(--collection-query-widget-row-gap)
10
+ ) * var(--collection-query-widget-visible-rows) - var(--collection-query-widget-row-gap)
11
+ );
12
+
13
+ flex-direction: column;
14
+ align-items: stretch;
15
+ gap: var(--spacer-2-5);
16
+ height: 100%;
17
+ /* Border + padding + header + header/rows gap + the visible row viewport. */
18
+ max-height: calc(
19
+ (var(--stroke-width-small) * 2) + (var(--spacer-2-5) * 3) +
20
+ var(--text-heading-small-line-height, 24px) +
21
+ var(--collection-query-widget-rows-height)
22
+ );
23
+ overflow: hidden;
24
+ padding: var(--spacer-2-5);
25
+ }
26
+
27
+ .collection-query-widget__title {
28
+ margin: 0;
29
+ color: var(--color-text);
30
+ font-family: var(--text-heading-small-font-family, var(--text-body-large-strong-font-family));
31
+ font-size: var(--text-heading-small-font-size, 20px);
32
+ font-weight: var(--text-heading-small-font-weight, var(--text-body-large-strong-font-weight));
33
+ line-height: var(--text-heading-small-line-height, 24px);
34
+ }
35
+
36
+ .collection-query-widget__header {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: var(--spacer-2);
40
+ }
41
+
42
+ .collection-query-widget__rows {
43
+ display: flex;
44
+ flex: 1 1 auto;
45
+ flex-direction: column;
46
+ gap: var(--collection-query-widget-row-gap);
47
+ min-height: 0;
48
+ overflow-y: auto;
49
+ padding: 0;
50
+ padding-inline-end: calc(
51
+ var(--collection-query-widget-scrollbar-width) + var(--spacer-1)
52
+ );
53
+ margin: 0;
54
+ list-style: none;
55
+ scrollbar-color: GrayText transparent;
56
+ scrollbar-width: thin;
57
+
58
+ &::-webkit-scrollbar {
59
+ width: var(--collection-query-widget-scrollbar-width);
60
+ background: transparent;
61
+ }
62
+
63
+ &::-webkit-scrollbar-corner,
64
+ &::-webkit-scrollbar-track,
65
+ &::-webkit-scrollbar-track-piece {
66
+ background: transparent;
67
+ }
68
+
69
+ &::-webkit-scrollbar-thumb {
70
+ border-radius: var(--radius-small, 3px);
71
+ background: GrayText;
72
+ }
73
+ }
74
+
75
+ .collection-query-widget__row {
76
+ margin: 0;
77
+ }
78
+
79
+ .collection-query-widget__row-link {
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: space-between;
83
+ gap: var(--spacer-2);
84
+ min-height: var(--collection-query-widget-row-height);
85
+ padding: var(--spacer-1) calc(var(--spacer-1) * 1.5);
86
+ border: var(--stroke-width-small) solid var(--color-border);
87
+ border-radius: var(--radius-small, 3px);
88
+ background: var(--color-bg);
89
+ color: var(--color-text);
90
+ text-decoration: none;
91
+
92
+ &:hover {
93
+ background: var(--color-bg-secondary-hover);
94
+ text-decoration: none;
95
+ }
96
+ }
97
+
98
+ .collection-query-widget__row-main,
99
+ .collection-query-widget__row-meta {
100
+ display: inline-flex;
101
+ align-items: center;
102
+ min-width: 0;
103
+ }
104
+
105
+ .collection-query-widget__row-main {
106
+ flex: 1 1 auto;
107
+ gap: calc(var(--spacer-1) * 1.5);
108
+ }
109
+
110
+ .collection-query-widget__row-meta {
111
+ flex: 0 0 auto;
112
+ gap: var(--spacer-1);
113
+ padding-left: calc(var(--spacer-1) * 1.5);
114
+ color: var(--color-text-secondary);
115
+ font-family: var(--text-body-small-font-family, var(--text-body-medium-font-family));
116
+ font-size: var(--text-body-small-font-size, 13px);
117
+ line-height: var(--text-body-small-line-height, 20px);
118
+ white-space: nowrap;
119
+ }
120
+
121
+ .collection-query-widget__row-title {
122
+ overflow: hidden;
123
+ color: var(--color-text);
124
+ font-family: var(
125
+ --text-body-small-strong-font-family,
126
+ var(--text-body-medium-strong-font-family)
127
+ );
128
+ font-size: var(--text-body-small-strong-font-size, 13px);
129
+ font-weight: var(
130
+ --text-body-small-strong-font-weight,
131
+ var(--text-body-medium-strong-font-weight)
132
+ );
133
+ line-height: var(--text-body-small-strong-line-height, 24px);
134
+ text-overflow: ellipsis;
135
+ white-space: nowrap;
136
+ }
137
+
138
+ .collection-query-widget--error {
139
+ border-color: var(--color-bg-danger);
140
+ }
141
+
142
+ .collection-query-widget--error .collection-query-widget__title {
143
+ color: var(--color-text-danger);
144
+ }
145
+
146
+ .collection-query-widget__error {
147
+ display: flex;
148
+ flex-direction: column;
149
+ gap: calc(var(--spacer-1) * 1.5);
150
+ padding: calc(var(--spacer-1) * 1.5);
151
+ border: var(--stroke-width-small) solid var(--color-bg-danger);
152
+ border-radius: var(--radius-small, 3px);
153
+ background: var(--color-bg-danger-tertiary);
154
+ color: var(--color-text-danger);
155
+ font-family: var(--text-body-small-font-family, var(--text-body-medium-font-family));
156
+ font-size: var(--text-body-small-font-size, 13px);
157
+ line-height: var(--text-body-small-line-height, 20px);
158
+ }
159
+
160
+ .collection-query-widget__error-title {
161
+ display: inline-flex;
162
+ width: fit-content;
163
+ padding: 0 var(--spacer-1);
164
+ margin: 0;
165
+ border-radius: var(--radius-small, 3px);
166
+ background: var(--color-bg-danger);
167
+ color: var(--color-text-ondanger);
168
+ font-family: var(--text-body-small-strong-font-family, var(--text-body-medium-strong-font-family));
169
+ font-size: var(--text-body-small-strong-font-size, 13px);
170
+ font-weight: var(
171
+ --text-body-small-strong-font-weight,
172
+ var(--text-body-medium-strong-font-weight)
173
+ );
174
+ line-height: var(--text-body-small-strong-line-height, 20px);
175
+ }
176
+
177
+ .collection-query-widget__error-list {
178
+ display: flex;
179
+ flex-direction: column;
180
+ gap: var(--spacer-1);
181
+ padding: 0;
182
+ margin: 0;
183
+ list-style: none;
184
+ }
185
+
186
+ .collection-query-widget__error-list li {
187
+ display: flex;
188
+ gap: var(--spacer-1);
189
+ min-width: 0;
190
+ }
191
+
192
+ .collection-query-widget__error-list li::before {
193
+ content: '';
194
+ width: 4px;
195
+ height: 4px;
196
+ flex: 0 0 auto;
197
+ margin-top: calc(var(--spacer-1) * 1.25);
198
+ border-radius: 50%;
199
+ background: currentColor;
200
+ }
201
+
202
+ .collection-query-widget__empty {
203
+ margin: 0;
204
+ color: var(--color-text-secondary);
205
+ }
206
+ }
@@ -0,0 +1,17 @@
1
+ import type { CollectionSlug, Where, WidgetServerProps } from 'payload';
2
+ import React from 'react';
3
+ import '../../elements/Card/index.css';
4
+ import './index.css';
5
+ type CollectionQueryWidgetData = {
6
+ limit?: number;
7
+ relatedCollection?: CollectionSlug;
8
+ sortDirection?: 'asc' | 'desc';
9
+ sortField?: string;
10
+ title?: string;
11
+ where?: Where;
12
+ };
13
+ export declare function CollectionQueryWidget({ req, widgetData, }: WidgetServerProps<{
14
+ data?: CollectionQueryWidgetData;
15
+ }>): Promise<React.JSX.Element>;
16
+ export {};
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/widgets/CollectionQuery/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAIvE,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,+BAA+B,CAAA;AACtC,OAAO,aAAa,CAAA;AAGpB,KAAK,yBAAyB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iBAAiB,CAAC,EAAE,cAAc,CAAA;IAClC,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAiBD,wBAAsB,qBAAqB,CAAC,EAC1C,GAAG,EACH,UAAU,GACX,EAAE,iBAAiB,CAAC;IAAE,IAAI,CAAC,EAAE,yBAAyB,CAAA;CAAE,CAAC,8BAmHzD"}