@payloadcms/next 3.69.0-internal.2883df2 → 3.69.0

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 (133) hide show
  1. package/dist/cjs/withPayload.cjs +1 -0
  2. package/dist/cjs/withPayload.cjs.map +2 -2
  3. package/dist/prod/styles.css +1 -1
  4. package/dist/routes/graphql/playground.d.ts.map +1 -1
  5. package/dist/routes/graphql/playground.js +6 -1
  6. package/dist/routes/graphql/playground.js.map +1 -1
  7. package/dist/routes/rest/index.d.ts.map +1 -1
  8. package/dist/routes/rest/index.js +5 -1
  9. package/dist/routes/rest/index.js.map +1 -1
  10. package/dist/templates/Default/index.d.ts.map +1 -1
  11. package/dist/templates/Default/index.js +20 -35
  12. package/dist/templates/Default/index.js.map +1 -1
  13. package/dist/utilities/handleServerFunctions.d.ts.map +1 -1
  14. package/dist/utilities/handleServerFunctions.js +4 -0
  15. package/dist/utilities/handleServerFunctions.js.map +1 -1
  16. package/dist/views/API/index.client.d.ts.map +1 -1
  17. package/dist/views/API/index.client.js +7 -5
  18. package/dist/views/API/index.client.js.map +1 -1
  19. package/dist/views/Account/ResetPreferences/index.d.ts +0 -1
  20. package/dist/views/Account/ResetPreferences/index.d.ts.map +1 -1
  21. package/dist/views/Account/ResetPreferences/index.js +34 -22
  22. package/dist/views/Account/ResetPreferences/index.js.map +1 -1
  23. package/dist/views/Account/Settings/index.d.ts.map +1 -1
  24. package/dist/views/Account/Settings/index.js +0 -3
  25. package/dist/views/Account/Settings/index.js.map +1 -1
  26. package/dist/views/Account/index.d.ts.map +1 -1
  27. package/dist/views/Account/index.js +5 -1
  28. package/dist/views/Account/index.js.map +1 -1
  29. package/dist/views/BrowseByFolder/buildView.d.ts.map +1 -1
  30. package/dist/views/BrowseByFolder/buildView.js +1 -2
  31. package/dist/views/BrowseByFolder/buildView.js.map +1 -1
  32. package/dist/views/CollectionFolders/buildView.d.ts.map +1 -1
  33. package/dist/views/CollectionFolders/buildView.js +1 -2
  34. package/dist/views/CollectionFolders/buildView.js.map +1 -1
  35. package/dist/views/CreateFirstUser/index.client.d.ts.map +1 -1
  36. package/dist/views/CreateFirstUser/index.client.js +6 -3
  37. package/dist/views/CreateFirstUser/index.client.js.map +1 -1
  38. package/dist/views/Dashboard/Default/ModularDashboard/DashboardStepNav.d.ts +19 -0
  39. package/dist/views/Dashboard/Default/ModularDashboard/DashboardStepNav.d.ts.map +1 -0
  40. package/dist/views/Dashboard/Default/ModularDashboard/DashboardStepNav.js +147 -0
  41. package/dist/views/Dashboard/Default/ModularDashboard/DashboardStepNav.js.map +1 -0
  42. package/dist/views/Dashboard/Default/ModularDashboard/index.client.d.ts +21 -0
  43. package/dist/views/Dashboard/Default/ModularDashboard/index.client.d.ts.map +1 -0
  44. package/dist/views/Dashboard/Default/ModularDashboard/index.client.js +431 -0
  45. package/dist/views/Dashboard/Default/ModularDashboard/index.client.js.map +1 -0
  46. package/dist/views/Dashboard/Default/ModularDashboard/index.d.ts +5 -0
  47. package/dist/views/Dashboard/Default/ModularDashboard/index.d.ts.map +1 -0
  48. package/dist/views/Dashboard/Default/ModularDashboard/index.js +87 -0
  49. package/dist/views/Dashboard/Default/ModularDashboard/index.js.map +1 -0
  50. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/RenderWidget.d.ts +14 -0
  51. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/RenderWidget.d.ts.map +1 -0
  52. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/RenderWidget.js +99 -0
  53. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/RenderWidget.js.map +1 -0
  54. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/getDefaultLayoutServerFn.d.ts +12 -0
  55. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/getDefaultLayoutServerFn.d.ts.map +1 -0
  56. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/getDefaultLayoutServerFn.js +58 -0
  57. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/getDefaultLayoutServerFn.js.map +1 -0
  58. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/renderWidgetServerFn.d.ts +20 -0
  59. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/renderWidgetServerFn.d.ts.map +1 -0
  60. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/renderWidgetServerFn.js +72 -0
  61. package/dist/views/Dashboard/Default/ModularDashboard/renderWidget/renderWidgetServerFn.js.map +1 -0
  62. package/dist/views/Dashboard/Default/ModularDashboard/useDashboardLayout.d.ts +20 -0
  63. package/dist/views/Dashboard/Default/ModularDashboard/useDashboardLayout.d.ts.map +1 -0
  64. package/dist/views/Dashboard/Default/ModularDashboard/useDashboardLayout.js +158 -0
  65. package/dist/views/Dashboard/Default/ModularDashboard/useDashboardLayout.js.map +1 -0
  66. package/dist/views/Dashboard/Default/ModularDashboard/utils/collisionDetection.d.ts +7 -0
  67. package/dist/views/Dashboard/Default/ModularDashboard/utils/collisionDetection.d.ts.map +1 -0
  68. package/dist/views/Dashboard/Default/ModularDashboard/utils/collisionDetection.js +38 -0
  69. package/dist/views/Dashboard/Default/ModularDashboard/utils/collisionDetection.js.map +1 -0
  70. package/dist/views/Dashboard/Default/ModularDashboard/utils/sensors.d.ts +2 -0
  71. package/dist/views/Dashboard/Default/ModularDashboard/utils/sensors.d.ts.map +1 -0
  72. package/dist/views/Dashboard/Default/ModularDashboard/utils/sensors.js +264 -0
  73. package/dist/views/Dashboard/Default/ModularDashboard/utils/sensors.js.map +1 -0
  74. package/dist/views/Dashboard/Default/index.d.ts +0 -1
  75. package/dist/views/Dashboard/Default/index.d.ts.map +1 -1
  76. package/dist/views/Dashboard/Default/index.js +35 -145
  77. package/dist/views/Dashboard/Default/index.js.map +1 -1
  78. package/dist/views/Dashboard/index.d.ts.map +1 -1
  79. package/dist/views/Dashboard/index.js +3 -52
  80. package/dist/views/Dashboard/index.js.map +1 -1
  81. package/dist/views/Document/getVersions.d.ts.map +1 -1
  82. package/dist/views/Document/getVersions.js +2 -1
  83. package/dist/views/Document/getVersions.js.map +1 -1
  84. package/dist/views/Document/index.d.ts.map +1 -1
  85. package/dist/views/Document/index.js +6 -5
  86. package/dist/views/Document/index.js.map +1 -1
  87. package/dist/views/List/handleServerFunction.d.ts.map +1 -1
  88. package/dist/views/List/handleServerFunction.js +4 -1
  89. package/dist/views/List/handleServerFunction.js.map +1 -1
  90. package/dist/views/Login/LoginForm/index.d.ts.map +1 -1
  91. package/dist/views/Login/LoginForm/index.js +4 -1
  92. package/dist/views/Login/LoginForm/index.js.map +1 -1
  93. package/dist/views/Logout/LogoutClient.d.ts.map +1 -1
  94. package/dist/views/Logout/LogoutClient.js +2 -1
  95. package/dist/views/Logout/LogoutClient.js.map +1 -1
  96. package/dist/views/NotFound/index.d.ts.map +1 -1
  97. package/dist/views/NotFound/index.js +2 -1
  98. package/dist/views/NotFound/index.js.map +1 -1
  99. package/dist/views/ResetPassword/ResetPasswordForm/index.d.ts.map +1 -1
  100. package/dist/views/ResetPassword/ResetPasswordForm/index.js +11 -10
  101. package/dist/views/ResetPassword/ResetPasswordForm/index.js.map +1 -1
  102. package/dist/views/Root/getRouteData.d.ts.map +1 -1
  103. package/dist/views/Root/getRouteData.js.map +1 -1
  104. package/dist/views/Root/index.d.ts.map +1 -1
  105. package/dist/views/Root/index.js +6 -5
  106. package/dist/views/Root/index.js.map +1 -1
  107. package/dist/views/Root/isPathMatchingRoute.d.ts.map +1 -1
  108. package/dist/views/Root/isPathMatchingRoute.js.map +1 -1
  109. package/dist/views/Verify/index.d.ts.map +1 -1
  110. package/dist/views/Verify/index.js +2 -1
  111. package/dist/views/Verify/index.js.map +1 -1
  112. package/dist/views/Version/Default/SetStepNav.d.ts.map +1 -1
  113. package/dist/views/Version/Default/SetStepNav.js.map +1 -1
  114. package/dist/views/Version/RenderFieldsToDiff/utilities/countChangedFields.spec.js +1 -0
  115. package/dist/views/Version/RenderFieldsToDiff/utilities/countChangedFields.spec.js.map +1 -1
  116. package/dist/views/Version/RenderFieldsToDiff/utilities/fieldHasChanges.spec.js +1 -0
  117. package/dist/views/Version/RenderFieldsToDiff/utilities/fieldHasChanges.spec.js.map +1 -1
  118. package/dist/views/Version/RenderFieldsToDiff/utilities/getFieldsForRowComparison.spec.js +1 -0
  119. package/dist/views/Version/RenderFieldsToDiff/utilities/getFieldsForRowComparison.spec.js.map +1 -1
  120. package/dist/views/Version/Restore/index.d.ts.map +1 -1
  121. package/dist/views/Version/Restore/index.js +5 -2
  122. package/dist/views/Version/Restore/index.js.map +1 -1
  123. package/dist/views/Versions/index.d.ts.map +1 -1
  124. package/dist/views/Versions/index.js +5 -2
  125. package/dist/views/Versions/index.js.map +1 -1
  126. package/dist/withPayload/withPayload.d.ts.map +1 -1
  127. package/dist/withPayload/withPayload.js +1 -1
  128. package/dist/withPayload/withPayload.js.map +1 -1
  129. package/package.json +9 -7
  130. package/dist/utilities/getVisibleEntities.d.ts +0 -5
  131. package/dist/utilities/getVisibleEntities.d.ts.map +0 -1
  132. package/dist/utilities/getVisibleEntities.js +0 -26
  133. package/dist/utilities/getVisibleEntities.js.map +0 -1
@@ -0,0 +1,99 @@
1
+ 'use client';
2
+
3
+ import { c as _c } from "react/compiler-runtime";
4
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
5
+ import { ShimmerEffect, useServerFunctions } from '@payloadcms/ui';
6
+ import React, { useCallback, useEffect, useRef } from 'react';
7
+ /**
8
+ * Utility to render a widget on-demand on the client.
9
+ */
10
+ export const RenderWidget = t0 => {
11
+ const $ = _c(9);
12
+ const {
13
+ widgetId
14
+ } = t0;
15
+ const [Component, setComponent] = React.useState(null);
16
+ const {
17
+ serverFunction
18
+ } = useServerFunctions();
19
+ let t1;
20
+ if ($[0] !== serverFunction || $[1] !== widgetId) {
21
+ t1 = () => {
22
+ const render = async function render() {
23
+ ;
24
+ try {
25
+ const widgetSlug = widgetId.slice(0, widgetId.lastIndexOf("-"));
26
+ const result = await serverFunction({
27
+ name: "render-widget",
28
+ args: {
29
+ widgetSlug
30
+ }
31
+ });
32
+ setComponent(result.component);
33
+ } catch (t2) {
34
+ setComponent(React.createElement("div", {
35
+ style: {
36
+ background: "var(--theme-error-50)",
37
+ border: "1px solid var(--theme-error-200)",
38
+ borderRadius: "4px",
39
+ color: "var(--theme-error-text)",
40
+ padding: "20px",
41
+ textAlign: "center"
42
+ }
43
+ }, "Failed to load widget. Please try again later."));
44
+ }
45
+ };
46
+ render();
47
+ };
48
+ $[0] = serverFunction;
49
+ $[1] = widgetId;
50
+ $[2] = t1;
51
+ } else {
52
+ t1 = $[2];
53
+ }
54
+ const renderWidget = t1;
55
+ const mounted = useRef(false);
56
+ let t2;
57
+ let t3;
58
+ if ($[3] !== renderWidget) {
59
+ t2 = () => {
60
+ if (mounted.current) {
61
+ return;
62
+ }
63
+ mounted.current = true;
64
+ renderWidget();
65
+ };
66
+ t3 = [renderWidget];
67
+ $[3] = renderWidget;
68
+ $[4] = t2;
69
+ $[5] = t3;
70
+ } else {
71
+ t2 = $[4];
72
+ t3 = $[5];
73
+ }
74
+ useEffect(t2, t3);
75
+ if (!Component) {
76
+ let t4;
77
+ if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
78
+ t4 = _jsx(ShimmerEffect, {
79
+ height: "100%"
80
+ });
81
+ $[6] = t4;
82
+ } else {
83
+ t4 = $[6];
84
+ }
85
+ return t4;
86
+ }
87
+ let t4;
88
+ if ($[7] !== Component) {
89
+ t4 = _jsx(_Fragment, {
90
+ children: Component
91
+ });
92
+ $[7] = Component;
93
+ $[8] = t4;
94
+ } else {
95
+ t4 = $[8];
96
+ }
97
+ return t4;
98
+ };
99
+ //# sourceMappingURL=RenderWidget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenderWidget.js","names":["c","_c","ShimmerEffect","useServerFunctions","React","useCallback","useEffect","useRef","RenderWidget","t0","$","widgetId","Component","setComponent","useState","serverFunction","t1","render","widgetSlug","slice","lastIndexOf","result","name","args","component","t2","createElement","style","background","border","borderRadius","color","padding","textAlign","renderWidget","mounted","t3","current","t4","Symbol","for","_jsx","height","_Fragment","children"],"sources":["../../../../../../src/views/Dashboard/Default/ModularDashboard/renderWidget/RenderWidget.tsx"],"sourcesContent":["'use client'\n\nimport { ShimmerEffect, useServerFunctions } from '@payloadcms/ui'\nimport React, { useCallback, useEffect, useRef } from 'react'\n\nimport type {\n RenderWidgetServerFnArgs,\n RenderWidgetServerFnReturnType,\n} from './renderWidgetServerFn.js'\n\n/**\n * Utility to render a widget on-demand on the client.\n */\nexport const RenderWidget: React.FC<{\n /**\n * Instance-specific data for this widget\n */\n // TODO: widgetData?: Record<string, unknown>\n /**\n * Unique ID for this widget instance (format: \"slug-timestamp\")\n */\n widgetId: string\n}> = ({ /* widgetData, */ widgetId }) => {\n const [Component, setComponent] = React.useState<null | React.ReactNode>(null)\n const { serverFunction } = useServerFunctions()\n\n const renderWidget = useCallback(() => {\n async function render() {\n try {\n const widgetSlug = widgetId.slice(0, widgetId.lastIndexOf('-'))\n\n const result = (await serverFunction({\n name: 'render-widget',\n args: {\n // TODO: widgets will support state in the future\n // widgetData,\n widgetSlug,\n } as RenderWidgetServerFnArgs,\n })) as RenderWidgetServerFnReturnType\n\n setComponent(result.component)\n } catch (error) {\n // Log error but don't expose details to console in production\n\n // Fallback error component\n setComponent(\n React.createElement(\n 'div',\n {\n style: {\n background: 'var(--theme-error-50)',\n border: '1px solid var(--theme-error-200)',\n borderRadius: '4px',\n color: 'var(--theme-error-text)',\n padding: '20px',\n textAlign: 'center',\n },\n },\n 'Failed to load widget. Please try again later.',\n ),\n )\n }\n }\n void render()\n }, [serverFunction, widgetId /* widgetData, */])\n\n const mounted = useRef(false)\n\n useEffect(() => {\n if (mounted.current) {\n return\n }\n mounted.current = true\n void renderWidget()\n }, [renderWidget])\n\n if (!Component) {\n return <ShimmerEffect height=\"100%\" />\n }\n\n return <>{Component}</>\n}\n"],"mappings":"AAAA;;AAAA,SAAAA,CAAA,IAAAC,EAAA;;AAEA,SAASC,aAAa,EAAEC,kBAAkB,QAAQ;AAClD,OAAOC,KAAA,IAASC,WAAW,EAAEC,SAAS,EAAEC,MAAM,QAAQ;AAOtD;;;AAGA,OAAO,MAAMC,YAAA,GASRC,EAAA;EAAA,MAAAC,CAAA,GAAAT,EAAA;EAAC;IAAAU;EAAA,IAAAF,EAA8B;EAClC,OAAAG,SAAA,EAAAC,YAAA,IAAkCT,KAAA,CAAAU,QAAA,KAAuC;EACzE;IAAAC;EAAA,IAA2BZ,kBAAA;EAAA,IAAAa,EAAA;EAAA,IAAAN,CAAA,QAAAK,cAAA,IAAAL,CAAA,QAAAC,QAAA;IAEMK,EAAA,GAAAA,CAAA;MAC/B,MAAAC,MAAA,kBAAAA,OAAA;QAAA;QAAA;UAEI,MAAAC,UAAA,GAAmBP,QAAA,CAAAQ,KAAA,IAAkBR,QAAA,CAAAS,WAAA,CAAqB;UAE1D,MAAAC,MAAA,SAAsBN,cAAA;YAAAO,IAAA,EACd;YAAAC,IAAA;cAAAL;YAAA;UAAA,CAMR;UAEAL,YAAA,CAAaQ,MAAA,CAAAG,SAAgB;QAAA,SAAAC,EAAA;UAK7BZ,YAAA,CACET,KAAA,CAAAsB,aAAA,CACE;YAAAC,KAAA;cAAAC,UAAA,EAGgB;cAAAC,MAAA,EACJ;cAAAC,YAAA,EACM;cAAAC,KAAA,EACP;cAAAC,OAAA,EACE;cAAAC,SAAA,EACE;YAAA;UAAA,GAGf;QAAA;MAAA;MAKHhB,MAAA;IAAA;IACPP,CAAA,MAAAK,cAAA;IAAAL,CAAA,MAAAC,QAAA;IAAAD,CAAA,MAAAM,EAAA;EAAA;IAAAA,EAAA,GAAAN,CAAA;EAAA;EAtCA,MAAAwB,YAAA,GAAqBlB,EAsC0B;EAE/C,MAAAmB,OAAA,GAAgB5B,MAAA,MAAO;EAAA,IAAAkB,EAAA;EAAA,IAAAW,EAAA;EAAA,IAAA1B,CAAA,QAAAwB,YAAA;IAEbT,EAAA,GAAAA,CAAA;MAAA,IACJU,OAAA,CAAAE,OAAA;QAAA;MAAA;MAGJF,OAAA,CAAAE,OAAA;MACKH,YAAA;IAAA;IACJE,EAAA,IAACF,YAAA;IAAaxB,CAAA,MAAAwB,YAAA;IAAAxB,CAAA,MAAAe,EAAA;IAAAf,CAAA,MAAA0B,EAAA;EAAA;IAAAX,EAAA,GAAAf,CAAA;IAAA0B,EAAA,GAAA1B,CAAA;EAAA;EANjBJ,SAAA,CAAUmB,EAMV,EAAGW,EAAc;EAAA,KAEZxB,SAAA;IAAA,IAAA0B,EAAA;IAAA,IAAA5B,CAAA,QAAA6B,MAAA,CAAAC,GAAA;MACIF,EAAA,GAAAG,IAAA,CAAAvC,aAAA;QAAAwC,MAAA,EAAsB;MAAA,C;;;;;WAAtBJ,E;;;;IAGFA,EAAA,GAAAG,IAAA,CAAAE,SAAA;MAAAC,QAAA,EAAGhC;IAAA,C;;;;;;SAAH0B,E;CACT","ignoreList":[]}
@@ -0,0 +1,12 @@
1
+ import type { ServerFunction } from 'payload';
2
+ import type { WidgetInstanceClient } from '../index.client.js';
3
+ export type GetDefaultLayoutServerFnArgs = Record<string, never>;
4
+ export type GetDefaultLayoutServerFnReturnType = {
5
+ layout: WidgetInstanceClient[];
6
+ };
7
+ /**
8
+ * Server function to get the default dashboard layout on-demand.
9
+ * Used when resetting the dashboard to its default configuration.
10
+ */
11
+ export declare const getDefaultLayoutHandler: ServerFunction<GetDefaultLayoutServerFnArgs, Promise<GetDefaultLayoutServerFnReturnType>>;
12
+ //# sourceMappingURL=getDefaultLayoutServerFn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getDefaultLayoutServerFn.d.ts","sourceRoot":"","sources":["../../../../../../src/views/Dashboard/Default/ModularDashboard/renderWidget/getDefaultLayoutServerFn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,cAAc,EAGf,MAAM,SAAS,CAAA;AAIhB,OAAO,KAAK,EAAE,oBAAoB,EAAc,MAAM,oBAAoB,CAAA;AAE1E,MAAM,MAAM,4BAA4B,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AAEhE,MAAM,MAAM,kCAAkC,GAAG;IAC/C,MAAM,EAAE,oBAAoB,EAAE,CAAA;CAC/B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAClD,4BAA4B,EAC5B,OAAO,CAAC,kCAAkC,CAAC,CA2B5C,CAAA"}
@@ -0,0 +1,58 @@
1
+ import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent';
2
+ /**
3
+ * Server function to get the default dashboard layout on-demand.
4
+ * Used when resetting the dashboard to its default configuration.
5
+ */
6
+ export const getDefaultLayoutHandler = async ({
7
+ req
8
+ }) => {
9
+ if (!req.user) {
10
+ throw new Error('Unauthorized');
11
+ }
12
+ const {
13
+ defaultLayout = [],
14
+ widgets = []
15
+ } = req.payload.config.admin.dashboard || {};
16
+ const {
17
+ importMap
18
+ } = req.payload;
19
+ const layoutItems = await getItemsFromConfig(defaultLayout, req, widgets);
20
+ const layout = layoutItems.map(layoutItem => {
21
+ const widgetSlug = layoutItem.id.slice(0, layoutItem.id.lastIndexOf('-'));
22
+ return {
23
+ component: RenderServerComponent({
24
+ Component: widgets.find(widget => widget.slug === widgetSlug)?.ComponentPath,
25
+ importMap,
26
+ serverProps: {
27
+ req,
28
+ widgetSlug
29
+ }
30
+ }),
31
+ item: layoutItem
32
+ };
33
+ });
34
+ return {
35
+ layout
36
+ };
37
+ };
38
+ async function getItemsFromConfig(defaultLayout, req, widgets) {
39
+ // Handle function format
40
+ let widgetInstances;
41
+ if (typeof defaultLayout === 'function') {
42
+ widgetInstances = await defaultLayout({
43
+ req
44
+ });
45
+ } else {
46
+ widgetInstances = defaultLayout;
47
+ }
48
+ return widgetInstances.map((widgetInstance, index) => {
49
+ const widget = widgets.find(w => w.slug === widgetInstance.widgetSlug);
50
+ return {
51
+ id: `${widgetInstance.widgetSlug}-${index}`,
52
+ maxWidth: widget?.maxWidth ?? 'full',
53
+ minWidth: widget?.minWidth ?? 'x-small',
54
+ width: widgetInstance.width || 'x-small'
55
+ };
56
+ });
57
+ }
58
+ //# sourceMappingURL=getDefaultLayoutServerFn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getDefaultLayoutServerFn.js","names":["RenderServerComponent","getDefaultLayoutHandler","req","user","Error","defaultLayout","widgets","payload","config","admin","dashboard","importMap","layoutItems","getItemsFromConfig","layout","map","layoutItem","widgetSlug","id","slice","lastIndexOf","component","Component","find","widget","slug","ComponentPath","serverProps","item","widgetInstances","widgetInstance","index","w","maxWidth","minWidth","width"],"sources":["../../../../../../src/views/Dashboard/Default/ModularDashboard/renderWidget/getDefaultLayoutServerFn.ts"],"sourcesContent":["import type {\n DashboardConfig,\n PayloadRequest,\n ServerFunction,\n Widget,\n WidgetServerProps,\n} from 'payload'\n\nimport { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'\n\nimport type { WidgetInstanceClient, WidgetItem } from '../index.client.js'\n\nexport type GetDefaultLayoutServerFnArgs = Record<string, never>\n\nexport type GetDefaultLayoutServerFnReturnType = {\n layout: WidgetInstanceClient[]\n}\n\n/**\n * Server function to get the default dashboard layout on-demand.\n * Used when resetting the dashboard to its default configuration.\n */\nexport const getDefaultLayoutHandler: ServerFunction<\n GetDefaultLayoutServerFnArgs,\n Promise<GetDefaultLayoutServerFnReturnType>\n> = async ({ req }) => {\n if (!req.user) {\n throw new Error('Unauthorized')\n }\n\n const { defaultLayout = [], widgets = [] } = req.payload.config.admin.dashboard || {}\n const { importMap } = req.payload\n\n const layoutItems = await getItemsFromConfig(defaultLayout, req, widgets)\n\n const layout: WidgetInstanceClient[] = layoutItems.map((layoutItem) => {\n const widgetSlug = layoutItem.id.slice(0, layoutItem.id.lastIndexOf('-'))\n return {\n component: RenderServerComponent({\n Component: widgets.find((widget) => widget.slug === widgetSlug)?.ComponentPath,\n importMap,\n serverProps: {\n req,\n widgetSlug,\n } satisfies WidgetServerProps,\n }),\n item: layoutItem,\n }\n })\n\n return { layout }\n}\n\nasync function getItemsFromConfig(\n defaultLayout: NonNullable<DashboardConfig['defaultLayout']>,\n req: PayloadRequest,\n widgets: Widget[],\n): Promise<WidgetItem[]> {\n // Handle function format\n let widgetInstances\n if (typeof defaultLayout === 'function') {\n widgetInstances = await defaultLayout({ req })\n } else {\n widgetInstances = defaultLayout\n }\n\n return widgetInstances.map((widgetInstance, index) => {\n const widget = widgets.find((w) => w.slug === widgetInstance.widgetSlug)\n return {\n id: `${widgetInstance.widgetSlug}-${index}`,\n maxWidth: widget?.maxWidth ?? 'full',\n minWidth: widget?.minWidth ?? 'x-small',\n width: widgetInstance.width || 'x-small',\n }\n })\n}\n"],"mappings":"AAQA,SAASA,qBAAqB,QAAQ;AAUtC;;;;AAIA,OAAO,MAAMC,uBAAA,GAGT,MAAAA,CAAO;EAAEC;AAAG,CAAE;EAChB,IAAI,CAACA,GAAA,CAAIC,IAAI,EAAE;IACb,MAAM,IAAIC,KAAA,CAAM;EAClB;EAEA,MAAM;IAAEC,aAAA,GAAgB,EAAE;IAAEC,OAAA,GAAU;EAAE,CAAE,GAAGJ,GAAA,CAAIK,OAAO,CAACC,MAAM,CAACC,KAAK,CAACC,SAAS,IAAI,CAAC;EACpF,MAAM;IAAEC;EAAS,CAAE,GAAGT,GAAA,CAAIK,OAAO;EAEjC,MAAMK,WAAA,GAAc,MAAMC,kBAAA,CAAmBR,aAAA,EAAeH,GAAA,EAAKI,OAAA;EAEjE,MAAMQ,MAAA,GAAiCF,WAAA,CAAYG,GAAG,CAAEC,UAAA;IACtD,MAAMC,UAAA,GAAaD,UAAA,CAAWE,EAAE,CAACC,KAAK,CAAC,GAAGH,UAAA,CAAWE,EAAE,CAACE,WAAW,CAAC;IACpE,OAAO;MACLC,SAAA,EAAWrB,qBAAA,CAAsB;QAC/BsB,SAAA,EAAWhB,OAAA,CAAQiB,IAAI,CAAEC,MAAA,IAAWA,MAAA,CAAOC,IAAI,KAAKR,UAAA,GAAaS,aAAA;QACjEf,SAAA;QACAgB,WAAA,EAAa;UACXzB,GAAA;UACAe;QACF;MACF;MACAW,IAAA,EAAMZ;IACR;EACF;EAEA,OAAO;IAAEF;EAAO;AAClB;AAEA,eAAeD,mBACbR,aAA4D,EAC5DH,GAAmB,EACnBI,OAAiB;EAEjB;EACA,IAAIuB,eAAA;EACJ,IAAI,OAAOxB,aAAA,KAAkB,YAAY;IACvCwB,eAAA,GAAkB,MAAMxB,aAAA,CAAc;MAAEH;IAAI;EAC9C,OAAO;IACL2B,eAAA,GAAkBxB,aAAA;EACpB;EAEA,OAAOwB,eAAA,CAAgBd,GAAG,CAAC,CAACe,cAAA,EAAgBC,KAAA;IAC1C,MAAMP,MAAA,GAASlB,OAAA,CAAQiB,IAAI,CAAES,CAAA,IAAMA,CAAA,CAAEP,IAAI,KAAKK,cAAA,CAAeb,UAAU;IACvE,OAAO;MACLC,EAAA,EAAI,GAAGY,cAAA,CAAeb,UAAU,IAAIc,KAAA,EAAO;MAC3CE,QAAA,EAAUT,MAAA,EAAQS,QAAA,IAAY;MAC9BC,QAAA,EAAUV,MAAA,EAAQU,QAAA,IAAY;MAC9BC,KAAA,EAAOL,cAAA,CAAeK,KAAK,IAAI;IACjC;EACF;AACF","ignoreList":[]}
@@ -0,0 +1,20 @@
1
+ import type { ServerFunction } from 'payload';
2
+ import React from 'react';
3
+ export type RenderWidgetServerFnArgs = {
4
+ /**
5
+ * Instance-specific data for this widget
6
+ */
7
+ /**
8
+ * The slug of the widget to render
9
+ */
10
+ widgetSlug: string;
11
+ };
12
+ export type RenderWidgetServerFnReturnType = {
13
+ component: React.ReactNode;
14
+ };
15
+ /**
16
+ * Server function to render a widget on-demand.
17
+ * Similar to render-field but specifically for dashboard widgets.
18
+ */
19
+ export declare const renderWidgetHandler: ServerFunction<RenderWidgetServerFnArgs, RenderWidgetServerFnReturnType>;
20
+ //# sourceMappingURL=renderWidgetServerFn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderWidgetServerFn.d.ts","sourceRoot":"","sources":["../../../../../../src/views/Dashboard/Default/ModularDashboard/renderWidget/renderWidgetServerFn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,SAAS,CAAA;AAGhE,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IAGH;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,8BAA8B,GAAG;IAC3C,SAAS,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,cAAc,CAC9C,wBAAwB,EACxB,8BAA8B,CAwE/B,CAAA"}
@@ -0,0 +1,72 @@
1
+ import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent';
2
+ import React from 'react';
3
+ /**
4
+ * Server function to render a widget on-demand.
5
+ * Similar to render-field but specifically for dashboard widgets.
6
+ */
7
+ export const renderWidgetHandler = ({
8
+ req,
9
+ /* widgetData, */widgetSlug
10
+ }) => {
11
+ if (!req.user) {
12
+ throw new Error('Unauthorized');
13
+ }
14
+ const {
15
+ widgets
16
+ } = req.payload.config.admin.dashboard;
17
+ const {
18
+ importMap
19
+ } = req.payload;
20
+ // Find the widget configuration
21
+ const widgetConfig = widgets.find(widget => widget.slug === widgetSlug);
22
+ if (!widgetConfig) {
23
+ return {
24
+ component: React.createElement('div', {
25
+ style: {
26
+ background: 'var(--theme-elevation-50)',
27
+ border: '1px solid var(--theme-elevation-200)',
28
+ borderRadius: '4px',
29
+ color: 'var(--theme-text)',
30
+ padding: '20px',
31
+ textAlign: 'center'
32
+ }
33
+ }, `Widget "${widgetSlug}" not found`)
34
+ };
35
+ }
36
+ try {
37
+ // Create server props for the widget
38
+ const serverProps = {
39
+ req,
40
+ // TODO: widgetData: widgetData || {},
41
+ widgetSlug
42
+ };
43
+ // Render the widget server component
44
+ const component = RenderServerComponent({
45
+ Component: widgetConfig.ComponentPath,
46
+ importMap,
47
+ serverProps
48
+ });
49
+ return {
50
+ component
51
+ };
52
+ } catch (error) {
53
+ const errorMessage = error instanceof Error ? error.message : String(error);
54
+ req.payload.logger.error({
55
+ err: error,
56
+ msg: `Error rendering widget "${widgetSlug}": ${errorMessage}`
57
+ });
58
+ return {
59
+ component: React.createElement('div', {
60
+ style: {
61
+ background: 'var(--theme-error-50)',
62
+ border: '1px solid var(--theme-error-200)',
63
+ borderRadius: '4px',
64
+ color: 'var(--theme-error-text)',
65
+ padding: '20px',
66
+ textAlign: 'center'
67
+ }
68
+ }, 'Error loading widget')
69
+ };
70
+ }
71
+ };
72
+ //# sourceMappingURL=renderWidgetServerFn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderWidgetServerFn.js","names":["RenderServerComponent","React","renderWidgetHandler","req","widgetSlug","user","Error","widgets","payload","config","admin","dashboard","importMap","widgetConfig","find","widget","slug","component","createElement","style","background","border","borderRadius","color","padding","textAlign","serverProps","Component","ComponentPath","error","errorMessage","message","String","logger","err","msg"],"sources":["../../../../../../src/views/Dashboard/Default/ModularDashboard/renderWidget/renderWidgetServerFn.ts"],"sourcesContent":["import type { ServerFunction, WidgetServerProps } from 'payload'\n\nimport { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'\nimport React from 'react'\n\nexport type RenderWidgetServerFnArgs = {\n /**\n * Instance-specific data for this widget\n */\n // TODO: widgets will support state in the future\n // widgetData?: Record<string, unknown>\n /**\n * The slug of the widget to render\n */\n widgetSlug: string\n}\n\nexport type RenderWidgetServerFnReturnType = {\n component: React.ReactNode\n}\n\n/**\n * Server function to render a widget on-demand.\n * Similar to render-field but specifically for dashboard widgets.\n */\nexport const renderWidgetHandler: ServerFunction<\n RenderWidgetServerFnArgs,\n RenderWidgetServerFnReturnType\n> = ({ req, /* widgetData, */ widgetSlug }) => {\n if (!req.user) {\n throw new Error('Unauthorized')\n }\n\n const { widgets } = req.payload.config.admin.dashboard\n const { importMap } = req.payload\n\n // Find the widget configuration\n const widgetConfig = widgets.find((widget) => widget.slug === widgetSlug)\n\n if (!widgetConfig) {\n return {\n component: React.createElement(\n 'div',\n {\n style: {\n background: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-200)',\n borderRadius: '4px',\n color: 'var(--theme-text)',\n padding: '20px',\n textAlign: 'center',\n },\n },\n `Widget \"${widgetSlug}\" not found`,\n ),\n }\n }\n\n try {\n // Create server props for the widget\n const serverProps: WidgetServerProps = {\n req,\n // TODO: widgetData: widgetData || {},\n widgetSlug,\n }\n\n // Render the widget server component\n const component = RenderServerComponent({\n Component: widgetConfig.ComponentPath,\n importMap,\n serverProps,\n })\n\n return { component }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n req.payload.logger.error({\n err: error,\n msg: `Error rendering widget \"${widgetSlug}\": ${errorMessage}`,\n })\n\n return {\n component: React.createElement(\n 'div',\n {\n style: {\n background: 'var(--theme-error-50)',\n border: '1px solid var(--theme-error-200)',\n borderRadius: '4px',\n color: 'var(--theme-error-text)',\n padding: '20px',\n textAlign: 'center',\n },\n },\n 'Error loading widget',\n ),\n }\n }\n}\n"],"mappings":"AAEA,SAASA,qBAAqB,QAAQ;AACtC,OAAOC,KAAA,MAAW;AAkBlB;;;;AAIA,OAAO,MAAMC,mBAAA,GAGTA,CAAC;EAAEC,GAAG;EAAE,iBAAkBC;AAAU,CAAE;EACxC,IAAI,CAACD,GAAA,CAAIE,IAAI,EAAE;IACb,MAAM,IAAIC,KAAA,CAAM;EAClB;EAEA,MAAM;IAAEC;EAAO,CAAE,GAAGJ,GAAA,CAAIK,OAAO,CAACC,MAAM,CAACC,KAAK,CAACC,SAAS;EACtD,MAAM;IAAEC;EAAS,CAAE,GAAGT,GAAA,CAAIK,OAAO;EAEjC;EACA,MAAMK,YAAA,GAAeN,OAAA,CAAQO,IAAI,CAAEC,MAAA,IAAWA,MAAA,CAAOC,IAAI,KAAKZ,UAAA;EAE9D,IAAI,CAACS,YAAA,EAAc;IACjB,OAAO;MACLI,SAAA,EAAWhB,KAAA,CAAMiB,aAAa,CAC5B,OACA;QACEC,KAAA,EAAO;UACLC,UAAA,EAAY;UACZC,MAAA,EAAQ;UACRC,YAAA,EAAc;UACdC,KAAA,EAAO;UACPC,OAAA,EAAS;UACTC,SAAA,EAAW;QACb;MACF,GACA,WAAWrB,UAAA,aAAuB;IAEtC;EACF;EAEA,IAAI;IACF;IACA,MAAMsB,WAAA,GAAiC;MACrCvB,GAAA;MACA;MACAC;IACF;IAEA;IACA,MAAMa,SAAA,GAAYjB,qBAAA,CAAsB;MACtC2B,SAAA,EAAWd,YAAA,CAAae,aAAa;MACrChB,SAAA;MACAc;IACF;IAEA,OAAO;MAAET;IAAU;EACrB,EAAE,OAAOY,KAAA,EAAO;IACd,MAAMC,YAAA,GAAeD,KAAA,YAAiBvB,KAAA,GAAQuB,KAAA,CAAME,OAAO,GAAGC,MAAA,CAAOH,KAAA;IAErE1B,GAAA,CAAIK,OAAO,CAACyB,MAAM,CAACJ,KAAK,CAAC;MACvBK,GAAA,EAAKL,KAAA;MACLM,GAAA,EAAK,2BAA2B/B,UAAA,MAAgB0B,YAAA;IAClD;IAEA,OAAO;MACLb,SAAA,EAAWhB,KAAA,CAAMiB,aAAa,CAC5B,OACA;QACEC,KAAA,EAAO;UACLC,UAAA,EAAY;UACZC,MAAA,EAAQ;UACRC,YAAA,EAAc;UACdC,KAAA,EAAO;UACPC,OAAA,EAAS;UACTC,SAAA,EAAW;QACb;MACF,GACA;IAEJ;EACF;AACF","ignoreList":[]}
@@ -0,0 +1,20 @@
1
+ import type { WidgetWidth } from 'payload';
2
+ import React from 'react';
3
+ import type { WidgetInstanceClient } from './index.client.js';
4
+ export declare function useDashboardLayout(initialLayout: WidgetInstanceClient[]): {
5
+ addWidget: (widgetSlug: string) => void;
6
+ cancel: () => void;
7
+ cancelModal: React.FunctionComponentElement<import("@payloadcms/ui/elements/ConfirmationModal").ConfirmationModalProps>;
8
+ currentLayout: WidgetInstanceClient[];
9
+ deleteWidget: (widgetId: string) => void;
10
+ isEditing: boolean;
11
+ moveWidget: ({ moveFromIndex, moveToIndex }: {
12
+ moveFromIndex: number;
13
+ moveToIndex: number;
14
+ }) => void;
15
+ resetLayout: () => Promise<void>;
16
+ resizeWidget: (widgetId: string, newWidth: WidgetWidth) => void;
17
+ saveLayout: () => Promise<void>;
18
+ setIsEditing: React.Dispatch<React.SetStateAction<boolean>>;
19
+ };
20
+ //# sourceMappingURL=useDashboardLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDashboardLayout.d.ts","sourceRoot":"","sources":["../../../../../src/views/Dashboard/Default/ModularDashboard/useDashboardLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAW1C,OAAO,KAAgC,MAAM,OAAO,CAAA;AAEpD,OAAO,KAAK,EAAE,oBAAoB,EAAc,MAAM,mBAAmB,CAAA;AAKzE,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,oBAAoB,EAAE;4BA4EvD,MAAM;;;;6BAiDR,MAAM;;iDA9DgB;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;;6BAwEpE,MAAM,YAAY,WAAW;;;EA0C3C"}
@@ -0,0 +1,158 @@
1
+ import { arrayMove } from '@dnd-kit/sortable';
2
+ import { ConfirmationModal, toast, useConfig, useModal, usePreferences, useServerFunctions } from '@payloadcms/ui';
3
+ import React, { useCallback, useState } from 'react';
4
+ import { RenderWidget } from './renderWidget/RenderWidget.js';
5
+ export function useDashboardLayout(initialLayout) {
6
+ const setLayoutPreference = useSetLayoutPreference();
7
+ const [isEditing, setIsEditing] = useState(false);
8
+ const {
9
+ widgets = []
10
+ } = useConfig().config.admin.dashboard ?? {};
11
+ const [currentLayout, setCurrentLayout] = useState(initialLayout);
12
+ const {
13
+ openModal
14
+ } = useModal();
15
+ const cancelModalSlug = 'cancel-dashboard-changes';
16
+ const {
17
+ serverFunction
18
+ } = useServerFunctions();
19
+ const saveLayout = useCallback(async () => {
20
+ try {
21
+ const layoutData = currentLayout.map(item => item.item);
22
+ setIsEditing(false);
23
+ await setLayoutPreference(layoutData);
24
+ } catch {
25
+ setIsEditing(true);
26
+ toast.error('Failed to save layout');
27
+ }
28
+ }, [setLayoutPreference, currentLayout]);
29
+ const resetLayout = useCallback(async () => {
30
+ try {
31
+ await setLayoutPreference(null);
32
+ const result = await serverFunction({
33
+ name: 'get-default-layout',
34
+ args: {}
35
+ });
36
+ setCurrentLayout(result.layout);
37
+ setIsEditing(false);
38
+ } catch {
39
+ toast.error('Failed to reset layout');
40
+ }
41
+ }, [setLayoutPreference, serverFunction]);
42
+ const performCancel = useCallback(() => {
43
+ setCurrentLayout(initialLayout);
44
+ setIsEditing(false);
45
+ }, [initialLayout]);
46
+ const cancel = useCallback(() => {
47
+ // Check if layout has changed
48
+ const hasChanges = currentLayout.length !== initialLayout.length || currentLayout.some((widget, index) => {
49
+ const initialWidget = initialLayout[index];
50
+ return !initialWidget || widget.item.id !== initialWidget.item.id || widget.item.width !== initialWidget.item.width;
51
+ });
52
+ // If there are changes, show confirmation modal
53
+ if (hasChanges) {
54
+ openModal(cancelModalSlug);
55
+ } else {
56
+ performCancel();
57
+ }
58
+ }, [currentLayout, initialLayout, openModal, cancelModalSlug, performCancel]);
59
+ const moveWidget = useCallback(({
60
+ moveFromIndex,
61
+ moveToIndex
62
+ }) => {
63
+ if (moveFromIndex === moveToIndex || moveFromIndex < 0 || moveToIndex < 0) {
64
+ return;
65
+ }
66
+ setCurrentLayout(prev => {
67
+ return arrayMove(prev, moveFromIndex, moveToIndex);
68
+ });
69
+ }, []);
70
+ const addWidget = useCallback(widgetSlug => {
71
+ if (!isEditing) {
72
+ return;
73
+ }
74
+ const widgetId = `${widgetSlug}-${Date.now()}`;
75
+ const widget = widgets.find(widget => widget.slug === widgetSlug);
76
+ // Create a new widget instance using RenderWidget
77
+ const newWidgetInstance = {
78
+ component: React.createElement(RenderWidget, {
79
+ widgetId
80
+ }),
81
+ item: {
82
+ id: widgetId,
83
+ maxWidth: widget?.maxWidth ?? 'full',
84
+ minWidth: widget?.minWidth ?? 'x-small',
85
+ width: widget?.minWidth ?? 'x-small'
86
+ }
87
+ };
88
+ setCurrentLayout(prev => [...prev, newWidgetInstance]);
89
+ // Scroll to the newly added widget after it's rendered and highlight it
90
+ setTimeout(() => {
91
+ const element = document.getElementById(widgetId);
92
+ if (element) {
93
+ element.scrollIntoView({
94
+ behavior: 'smooth',
95
+ block: 'center'
96
+ });
97
+ // Add highlight animation to the widget element
98
+ const widget = element.closest('.widget');
99
+ if (widget) {
100
+ widget.classList.add('widget--highlight');
101
+ // Remove the class after animation completes (1.5s fade out)
102
+ setTimeout(() => {
103
+ widget.classList.remove('widget--highlight');
104
+ }, 1500);
105
+ }
106
+ }
107
+ }, 100);
108
+ }, [isEditing, widgets]);
109
+ const deleteWidget = useCallback(widgetId => {
110
+ if (!isEditing) {
111
+ return;
112
+ }
113
+ setCurrentLayout(prev => prev.filter(item => item.item.id !== widgetId));
114
+ }, [isEditing]);
115
+ const resizeWidget = useCallback((widgetId, newWidth) => {
116
+ if (!isEditing) {
117
+ return;
118
+ }
119
+ setCurrentLayout(prev => prev.map(item => item.item.id === widgetId ? {
120
+ ...item,
121
+ item: {
122
+ ...item.item,
123
+ width: newWidth
124
+ }
125
+ } : item));
126
+ }, [isEditing]);
127
+ const cancelModal = React.createElement(ConfirmationModal, {
128
+ body: 'You have unsaved changes to your dashboard layout. Are you sure you want to discard them?',
129
+ confirmLabel: 'Discard',
130
+ heading: 'Discard changes?',
131
+ modalSlug: cancelModalSlug,
132
+ onConfirm: performCancel
133
+ });
134
+ return {
135
+ addWidget,
136
+ cancel,
137
+ cancelModal,
138
+ currentLayout,
139
+ deleteWidget,
140
+ isEditing,
141
+ moveWidget,
142
+ resetLayout,
143
+ resizeWidget,
144
+ saveLayout,
145
+ setIsEditing
146
+ };
147
+ }
148
+ function useSetLayoutPreference() {
149
+ const {
150
+ setPreference
151
+ } = usePreferences();
152
+ return useCallback(async layout => {
153
+ await setPreference('dashboard-layout', {
154
+ layouts: layout
155
+ }, false);
156
+ }, [setPreference]);
157
+ }
158
+ //# sourceMappingURL=useDashboardLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDashboardLayout.js","names":["arrayMove","ConfirmationModal","toast","useConfig","useModal","usePreferences","useServerFunctions","React","useCallback","useState","RenderWidget","useDashboardLayout","initialLayout","setLayoutPreference","useSetLayoutPreference","isEditing","setIsEditing","widgets","config","admin","dashboard","currentLayout","setCurrentLayout","openModal","cancelModalSlug","serverFunction","saveLayout","layoutData","map","item","error","resetLayout","result","name","args","layout","performCancel","cancel","hasChanges","length","some","widget","index","initialWidget","id","width","moveWidget","moveFromIndex","moveToIndex","prev","addWidget","widgetSlug","widgetId","Date","now","find","slug","newWidgetInstance","component","createElement","maxWidth","minWidth","setTimeout","element","document","getElementById","scrollIntoView","behavior","block","closest","classList","add","remove","deleteWidget","filter","resizeWidget","newWidth","cancelModal","body","confirmLabel","heading","modalSlug","onConfirm","setPreference","layouts"],"sources":["../../../../../src/views/Dashboard/Default/ModularDashboard/useDashboardLayout.ts"],"sourcesContent":["import type { WidgetWidth } from 'payload'\n\nimport { arrayMove } from '@dnd-kit/sortable'\nimport {\n ConfirmationModal,\n toast,\n useConfig,\n useModal,\n usePreferences,\n useServerFunctions,\n} from '@payloadcms/ui'\nimport React, { useCallback, useState } from 'react'\n\nimport type { WidgetInstanceClient, WidgetItem } from './index.client.js'\nimport type { GetDefaultLayoutServerFnReturnType } from './renderWidget/getDefaultLayoutServerFn.js'\n\nimport { RenderWidget } from './renderWidget/RenderWidget.js'\n\nexport function useDashboardLayout(initialLayout: WidgetInstanceClient[]) {\n const setLayoutPreference = useSetLayoutPreference()\n const [isEditing, setIsEditing] = useState(false)\n const { widgets = [] } = useConfig().config.admin.dashboard ?? {}\n const [currentLayout, setCurrentLayout] = useState<WidgetInstanceClient[]>(initialLayout)\n const { openModal } = useModal()\n const cancelModalSlug = 'cancel-dashboard-changes'\n const { serverFunction } = useServerFunctions()\n\n const saveLayout = useCallback(async () => {\n try {\n const layoutData: WidgetItem[] = currentLayout.map((item) => item.item)\n setIsEditing(false)\n await setLayoutPreference(layoutData)\n } catch {\n setIsEditing(true)\n toast.error('Failed to save layout')\n }\n }, [setLayoutPreference, currentLayout])\n\n const resetLayout = useCallback(async () => {\n try {\n await setLayoutPreference(null)\n\n const result = (await serverFunction({\n name: 'get-default-layout',\n args: {},\n })) as GetDefaultLayoutServerFnReturnType\n\n setCurrentLayout(result.layout)\n setIsEditing(false)\n } catch {\n toast.error('Failed to reset layout')\n }\n }, [setLayoutPreference, serverFunction])\n\n const performCancel = useCallback(() => {\n setCurrentLayout(initialLayout)\n setIsEditing(false)\n }, [initialLayout])\n\n const cancel = useCallback(() => {\n // Check if layout has changed\n const hasChanges =\n currentLayout.length !== initialLayout.length ||\n currentLayout.some((widget, index) => {\n const initialWidget = initialLayout[index]\n return (\n !initialWidget ||\n widget.item.id !== initialWidget.item.id ||\n widget.item.width !== initialWidget.item.width\n )\n })\n\n // If there are changes, show confirmation modal\n if (hasChanges) {\n openModal(cancelModalSlug)\n } else {\n performCancel()\n }\n }, [currentLayout, initialLayout, openModal, cancelModalSlug, performCancel])\n\n const moveWidget = useCallback(\n ({ moveFromIndex, moveToIndex }: { moveFromIndex: number; moveToIndex: number }) => {\n if (moveFromIndex === moveToIndex || moveFromIndex < 0 || moveToIndex < 0) {\n return\n }\n\n setCurrentLayout((prev) => {\n return arrayMove(prev, moveFromIndex, moveToIndex)\n })\n },\n [],\n )\n\n const addWidget = useCallback(\n (widgetSlug: string) => {\n if (!isEditing) {\n return\n }\n\n const widgetId = `${widgetSlug}-${Date.now()}`\n const widget = widgets.find((widget) => widget.slug === widgetSlug)\n\n // Create a new widget instance using RenderWidget\n const newWidgetInstance: WidgetInstanceClient = {\n component: React.createElement(RenderWidget, {\n widgetId,\n // TODO: widgetData can be added here for custom props\n }),\n item: {\n id: widgetId,\n maxWidth: widget?.maxWidth ?? 'full',\n minWidth: widget?.minWidth ?? 'x-small',\n width: widget?.minWidth ?? 'x-small',\n },\n }\n\n setCurrentLayout((prev) => [...prev, newWidgetInstance])\n\n // Scroll to the newly added widget after it's rendered and highlight it\n setTimeout(() => {\n const element = document.getElementById(widgetId)\n if (element) {\n element.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n })\n\n // Add highlight animation to the widget element\n const widget = element.closest('.widget')\n if (widget) {\n widget.classList.add('widget--highlight')\n // Remove the class after animation completes (1.5s fade out)\n setTimeout(() => {\n widget.classList.remove('widget--highlight')\n }, 1500)\n }\n }\n }, 100)\n },\n [isEditing, widgets],\n )\n\n const deleteWidget = useCallback(\n (widgetId: string) => {\n if (!isEditing) {\n return\n }\n setCurrentLayout((prev) => prev.filter((item) => item.item.id !== widgetId))\n },\n [isEditing],\n )\n\n const resizeWidget = useCallback(\n (widgetId: string, newWidth: WidgetWidth) => {\n if (!isEditing) {\n return\n }\n setCurrentLayout((prev) =>\n prev.map((item) =>\n item.item.id === widgetId\n ? {\n ...item,\n item: {\n ...item.item,\n width: newWidth,\n } satisfies WidgetItem,\n }\n : item,\n ),\n )\n },\n [isEditing],\n )\n\n const cancelModal = React.createElement(ConfirmationModal, {\n body: 'You have unsaved changes to your dashboard layout. Are you sure you want to discard them?',\n confirmLabel: 'Discard',\n heading: 'Discard changes?',\n modalSlug: cancelModalSlug,\n onConfirm: performCancel,\n })\n\n return {\n addWidget,\n cancel,\n cancelModal,\n currentLayout,\n deleteWidget,\n isEditing,\n moveWidget,\n resetLayout,\n resizeWidget,\n saveLayout,\n setIsEditing,\n }\n}\n\nfunction useSetLayoutPreference() {\n const { setPreference } = usePreferences()\n return useCallback(\n async (layout: null | WidgetItem[]) => {\n await setPreference('dashboard-layout', { layouts: layout }, false)\n },\n [setPreference],\n )\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,7 @@
1
+ import type { CollisionDetection } from '@dnd-kit/core';
2
+ /**
3
+ * Collision detection that considers the X
4
+ * axis only with respect to the position of the pointer (or collisionRect for keyboard)
5
+ */
6
+ export declare const closestInXAxis: CollisionDetection;
7
+ //# sourceMappingURL=collisionDetection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collisionDetection.d.ts","sourceRoot":"","sources":["../../../../../../src/views/Dashboard/Default/ModularDashboard/utils/collisionDetection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAEvD;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,kBAiC5B,CAAA"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Collision detection that considers the X
3
+ * axis only with respect to the position of the pointer (or collisionRect for keyboard)
4
+ */export const closestInXAxis = args => {
5
+ const collisions = [];
6
+ // Use pointer coordinates if available (mouse/touch), otherwise use collisionRect center (keyboard)
7
+ let x;
8
+ let y;
9
+ if (args.pointerCoordinates) {
10
+ x = args.pointerCoordinates.x;
11
+ y = args.pointerCoordinates.y;
12
+ } else if (args.collisionRect) {
13
+ // For keyboard navigation, use the center of the collisionRect
14
+ x = args.collisionRect.left + args.collisionRect.width / 2;
15
+ y = args.collisionRect.top + args.collisionRect.height / 2;
16
+ } else {
17
+ return [];
18
+ }
19
+ for (const container of args.droppableContainers) {
20
+ const rect = args.droppableRects.get(container.id);
21
+ if (!rect) {
22
+ continue;
23
+ }
24
+ // Only consider widgets in the same row (same Y axis)
25
+ if (y >= rect.top && y <= rect.bottom) {
26
+ const centerX = rect.left + rect.width / 2;
27
+ const distance = Math.abs(x - centerX);
28
+ collisions.push({
29
+ id: String(container.id),
30
+ data: {
31
+ value: distance
32
+ }
33
+ });
34
+ }
35
+ }
36
+ return collisions.sort((a, b) => a.data.value - b.data.value);
37
+ };
38
+ //# sourceMappingURL=collisionDetection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collisionDetection.js","names":["closestInXAxis","args","collisions","x","y","pointerCoordinates","collisionRect","left","width","top","height","container","droppableContainers","rect","droppableRects","get","id","bottom","centerX","distance","Math","abs","push","String","data","value","sort","a","b"],"sources":["../../../../../../src/views/Dashboard/Default/ModularDashboard/utils/collisionDetection.ts"],"sourcesContent":["import type { CollisionDetection } from '@dnd-kit/core'\n\n/**\n * Collision detection that considers the X\n * axis only with respect to the position of the pointer (or collisionRect for keyboard)\n */\nexport const closestInXAxis: CollisionDetection = (args) => {\n const collisions: Array<{ data: { value: number }; id: string }> = []\n\n // Use pointer coordinates if available (mouse/touch), otherwise use collisionRect center (keyboard)\n let x: number\n let y: number\n\n if (args.pointerCoordinates) {\n x = args.pointerCoordinates.x\n y = args.pointerCoordinates.y\n } else if (args.collisionRect) {\n // For keyboard navigation, use the center of the collisionRect\n x = args.collisionRect.left + args.collisionRect.width / 2\n y = args.collisionRect.top + args.collisionRect.height / 2\n } else {\n return []\n }\n\n for (const container of args.droppableContainers) {\n const rect = args.droppableRects.get(container.id)\n if (!rect) {\n continue\n }\n\n // Only consider widgets in the same row (same Y axis)\n if (y >= rect.top && y <= rect.bottom) {\n const centerX = rect.left + rect.width / 2\n const distance = Math.abs(x - centerX)\n collisions.push({ id: String(container.id), data: { value: distance } })\n }\n }\n\n return collisions.sort((a, b) => a.data.value - b.data.value)\n}\n"],"mappings":"AAEA;;;GAIA,OAAO,MAAMA,cAAA,GAAsCC,IAAA;EACjD,MAAMC,UAAA,GAA6D,EAAE;EAErE;EACA,IAAIC,CAAA;EACJ,IAAIC,CAAA;EAEJ,IAAIH,IAAA,CAAKI,kBAAkB,EAAE;IAC3BF,CAAA,GAAIF,IAAA,CAAKI,kBAAkB,CAACF,CAAC;IAC7BC,CAAA,GAAIH,IAAA,CAAKI,kBAAkB,CAACD,CAAC;EAC/B,OAAO,IAAIH,IAAA,CAAKK,aAAa,EAAE;IAC7B;IACAH,CAAA,GAAIF,IAAA,CAAKK,aAAa,CAACC,IAAI,GAAGN,IAAA,CAAKK,aAAa,CAACE,KAAK,GAAG;IACzDJ,CAAA,GAAIH,IAAA,CAAKK,aAAa,CAACG,GAAG,GAAGR,IAAA,CAAKK,aAAa,CAACI,MAAM,GAAG;EAC3D,OAAO;IACL,OAAO,EAAE;EACX;EAEA,KAAK,MAAMC,SAAA,IAAaV,IAAA,CAAKW,mBAAmB,EAAE;IAChD,MAAMC,IAAA,GAAOZ,IAAA,CAAKa,cAAc,CAACC,GAAG,CAACJ,SAAA,CAAUK,EAAE;IACjD,IAAI,CAACH,IAAA,EAAM;MACT;IACF;IAEA;IACA,IAAIT,CAAA,IAAKS,IAAA,CAAKJ,GAAG,IAAIL,CAAA,IAAKS,IAAA,CAAKI,MAAM,EAAE;MACrC,MAAMC,OAAA,GAAUL,IAAA,CAAKN,IAAI,GAAGM,IAAA,CAAKL,KAAK,GAAG;MACzC,MAAMW,QAAA,GAAWC,IAAA,CAAKC,GAAG,CAAClB,CAAA,GAAIe,OAAA;MAC9BhB,UAAA,CAAWoB,IAAI,CAAC;QAAEN,EAAA,EAAIO,MAAA,CAAOZ,SAAA,CAAUK,EAAE;QAAGQ,IAAA,EAAM;UAAEC,KAAA,EAAON;QAAS;MAAE;IACxE;EACF;EAEA,OAAOjB,UAAA,CAAWwB,IAAI,CAAC,CAACC,CAAA,EAAGC,CAAA,KAAMD,CAAA,CAAEH,IAAI,CAACC,KAAK,GAAGG,CAAA,CAAEJ,IAAI,CAACC,KAAK;AAC9D","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export declare function useDashboardSensors(): import("@dnd-kit/core").SensorDescriptor<import("@dnd-kit/core").SensorOptions>[];
2
+ //# sourceMappingURL=sensors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensors.d.ts","sourceRoot":"","sources":["../../../../../../src/views/Dashboard/Default/ModularDashboard/utils/sensors.ts"],"names":[],"mappings":"AAmTA,wBAAgB,mBAAmB,sFAWlC"}