@payloadcms/ui 4.0.0-canary.6 → 4.0.0-canary.7

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 (64) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/elements/RelationshipTable/index.css +1 -2
  3. package/dist/elements/Table/index.css +7 -2
  4. package/dist/exports/client/index.d.ts +1 -0
  5. package/dist/exports/client/index.d.ts.map +1 -1
  6. package/dist/exports/client/index.js +13 -13
  7. package/dist/exports/client/index.js.map +4 -4
  8. package/dist/exports/rsc/index.d.ts +2 -0
  9. package/dist/exports/rsc/index.d.ts.map +1 -1
  10. package/dist/exports/rsc/index.js +2 -0
  11. package/dist/exports/rsc/index.js.map +1 -1
  12. package/dist/forms/fieldSchemasToFormState/addFieldStatePromise.d.ts +1 -0
  13. package/dist/forms/fieldSchemasToFormState/addFieldStatePromise.d.ts.map +1 -1
  14. package/dist/forms/fieldSchemasToFormState/addFieldStatePromise.js +9 -1
  15. package/dist/forms/fieldSchemasToFormState/addFieldStatePromise.js.map +1 -1
  16. package/dist/forms/fieldSchemasToFormState/index.d.ts +2 -1
  17. package/dist/forms/fieldSchemasToFormState/index.d.ts.map +1 -1
  18. package/dist/forms/fieldSchemasToFormState/index.js +2 -0
  19. package/dist/forms/fieldSchemasToFormState/index.js.map +1 -1
  20. package/dist/forms/fieldSchemasToFormState/iterateFields.d.ts +2 -1
  21. package/dist/forms/fieldSchemasToFormState/iterateFields.d.ts.map +1 -1
  22. package/dist/forms/fieldSchemasToFormState/iterateFields.js +2 -0
  23. package/dist/forms/fieldSchemasToFormState/iterateFields.js.map +1 -1
  24. package/dist/styles.css +1 -1
  25. package/dist/utilities/buildFormState.d.ts.map +1 -1
  26. package/dist/utilities/buildFormState.js +1 -0
  27. package/dist/utilities/buildFormState.js.map +1 -1
  28. package/dist/utilities/formatRelativeDate.d.ts +15 -0
  29. package/dist/utilities/formatRelativeDate.d.ts.map +1 -0
  30. package/dist/utilities/formatRelativeDate.js +62 -0
  31. package/dist/utilities/formatRelativeDate.js.map +1 -0
  32. package/dist/utilities/recentlyViewed.d.ts +16 -0
  33. package/dist/utilities/recentlyViewed.d.ts.map +1 -0
  34. package/dist/utilities/recentlyViewed.js +21 -0
  35. package/dist/utilities/recentlyViewed.js.map +1 -0
  36. package/dist/utilities/recentlyViewed.spec.js +92 -0
  37. package/dist/utilities/recentlyViewed.spec.js.map +1 -0
  38. package/dist/views/Document/index.d.ts.map +1 -1
  39. package/dist/views/Document/index.js +9 -0
  40. package/dist/views/Document/index.js.map +1 -1
  41. package/dist/views/Document/recordRecentlyViewed.d.ts +12 -0
  42. package/dist/views/Document/recordRecentlyViewed.d.ts.map +1 -0
  43. package/dist/views/Document/recordRecentlyViewed.js +40 -0
  44. package/dist/views/Document/recordRecentlyViewed.js.map +1 -0
  45. package/dist/widgets/CollectionQuery/index.css +10 -152
  46. package/dist/widgets/CollectionQuery/index.d.ts +0 -1
  47. package/dist/widgets/CollectionQuery/index.d.ts.map +1 -1
  48. package/dist/widgets/CollectionQuery/index.js +40 -111
  49. package/dist/widgets/CollectionQuery/index.js.map +1 -1
  50. package/dist/widgets/RecentlyViewed/CollectionsField/index.css +14 -0
  51. package/dist/widgets/RecentlyViewed/CollectionsField/index.d.ts +10 -0
  52. package/dist/widgets/RecentlyViewed/CollectionsField/index.d.ts.map +1 -0
  53. package/dist/widgets/RecentlyViewed/CollectionsField/index.js +76 -0
  54. package/dist/widgets/RecentlyViewed/CollectionsField/index.js.map +1 -0
  55. package/dist/widgets/RecentlyViewed/index.d.ts +10 -0
  56. package/dist/widgets/RecentlyViewed/index.d.ts.map +1 -0
  57. package/dist/widgets/RecentlyViewed/index.js +148 -0
  58. package/dist/widgets/RecentlyViewed/index.js.map +1 -0
  59. package/dist/widgets/WidgetCard/index.css +155 -0
  60. package/dist/widgets/WidgetCard/index.d.ts +19 -0
  61. package/dist/widgets/WidgetCard/index.d.ts.map +1 -0
  62. package/dist/widgets/WidgetCard/index.js +49 -0
  63. package/dist/widgets/WidgetCard/index.js.map +1 -0
  64. package/package.json +5 -5
@@ -0,0 +1,148 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { getTranslation } from '@payloadcms/translations';
3
+ import { formatAdminURL, PREFERENCE_KEYS } from 'payload/shared';
4
+ import React from 'react';
5
+ import { formatRelativeDate, getRelativeTimeFormat } from '../../utilities/formatRelativeDate.js';
6
+ import { getPreferences } from '../../utilities/upsertPreferences.js';
7
+ import { WidgetCard, WidgetList } from '../WidgetCard/index.js';
8
+ const displayLimit = 10;
9
+ export async function RecentlyViewedWidget({
10
+ req,
11
+ widgetData
12
+ }) {
13
+ const {
14
+ i18n,
15
+ user
16
+ } = req;
17
+ const title = i18n.t('dashboard:widgetRecentlyViewedTitle');
18
+ const enrichedItems = user ? await getEnrichedItems({
19
+ req,
20
+ widgetData
21
+ }) : [];
22
+ const relativeTimeFormat = getRelativeTimeFormat(i18n.language);
23
+ const items = enrichedItems.map(item => ({
24
+ href: item.href,
25
+ key: `${item.collectionSlug}:${item.id}`,
26
+ main: /*#__PURE__*/_jsxs(_Fragment, {
27
+ children: [item.thumbnailURL ? /*#__PURE__*/_jsx("img", {
28
+ alt: "",
29
+ className: "widget-card__thumbnail",
30
+ src: item.thumbnailURL
31
+ }) : /*#__PURE__*/_jsx("span", {
32
+ className: "widget-card__type",
33
+ children: item.typeLabel
34
+ }), /*#__PURE__*/_jsx("span", {
35
+ className: "widget-card__row-title",
36
+ children: item.title
37
+ })]
38
+ }),
39
+ meta: /*#__PURE__*/_jsx("time", {
40
+ dateTime: item.viewedAt,
41
+ children: formatRelativeDate({
42
+ relativeTimeFormat,
43
+ value: item.viewedAt
44
+ })
45
+ })
46
+ }));
47
+ return /*#__PURE__*/_jsx(WidgetCard, {
48
+ className: "recently-viewed-widget",
49
+ title: title,
50
+ children: /*#__PURE__*/_jsx(WidgetList, {
51
+ emptyMessage: i18n.t('dashboard:widgetRecentlyViewedEmpty'),
52
+ items: items
53
+ })
54
+ });
55
+ }
56
+ /**
57
+ * Loads the stored recently viewed identities and computes display fields from the live documents.
58
+ * Documents are queried with access control, so deleted or now-forbidden docs are dropped.
59
+ */
60
+ async function getEnrichedItems({
61
+ req,
62
+ widgetData
63
+ }) {
64
+ const {
65
+ i18n,
66
+ payload,
67
+ user
68
+ } = req;
69
+ const excludedCollections = new Set(widgetData?.excludedCollections ?? []);
70
+ const preference = await getPreferences(PREFERENCE_KEYS.RECENTLY_VIEWED, payload, user.id, user.collection);
71
+ const items = (preference?.value?.items ?? []).filter(item => !excludedCollections.has(item.collectionSlug) && Boolean(payload.collections[item.collectionSlug]));
72
+ if (items.length === 0) {
73
+ return [];
74
+ }
75
+ const idsByCollection = new Map();
76
+ for (const item of items) {
77
+ const ids = idsByCollection.get(item.collectionSlug) ?? [];
78
+ ids.push(item.id);
79
+ idsByCollection.set(item.collectionSlug, ids);
80
+ }
81
+ const docsByKey = new Map();
82
+ await Promise.all([...idsByCollection.entries()].map(async ([collectionSlug, ids]) => {
83
+ try {
84
+ const result = await payload.find({
85
+ collection: collectionSlug,
86
+ depth: 0,
87
+ limit: ids.length,
88
+ overrideAccess: false,
89
+ pagination: false,
90
+ user,
91
+ where: {
92
+ id: {
93
+ in: ids
94
+ }
95
+ }
96
+ });
97
+ for (const doc of result.docs) {
98
+ docsByKey.set(`${collectionSlug}:${doc.id}`, doc);
99
+ }
100
+ } catch (err) {
101
+ payload.logger.error({
102
+ err,
103
+ msg: `RecentlyViewedWidget: failed to load collection "${collectionSlug}"`
104
+ });
105
+ }
106
+ }));
107
+ const enrichedItems = [];
108
+ for (const item of items) {
109
+ if (enrichedItems.length >= displayLimit) {
110
+ break;
111
+ }
112
+ const doc = docsByKey.get(`${item.collectionSlug}:${item.id}`);
113
+ if (!doc) {
114
+ continue;
115
+ }
116
+ const collectionConfig = payload.collections[item.collectionSlug].config;
117
+ const useAsTitle = collectionConfig.admin?.useAsTitle || 'id';
118
+ const rawTitle = getValueByPath({
119
+ object: doc,
120
+ path: useAsTitle
121
+ });
122
+ enrichedItems.push({
123
+ id: item.id,
124
+ collectionSlug: item.collectionSlug,
125
+ href: formatAdminURL({
126
+ adminRoute: payload.config.routes.admin,
127
+ path: `/collections/${item.collectionSlug}/${item.id}`
128
+ }),
129
+ thumbnailURL: collectionConfig.upload && typeof doc.thumbnailURL === 'string' ? doc.thumbnailURL : undefined,
130
+ title: typeof rawTitle === 'string' && rawTitle ? rawTitle : String(item.id),
131
+ typeLabel: getTranslation(collectionConfig.labels.singular, i18n),
132
+ viewedAt: item.viewedAt
133
+ });
134
+ }
135
+ return enrichedItems;
136
+ }
137
+ function getValueByPath({
138
+ object,
139
+ path
140
+ }) {
141
+ return path.split('.').reduce((value, segment) => {
142
+ if (!value || typeof value !== 'object') {
143
+ return undefined;
144
+ }
145
+ return value[segment];
146
+ }, object);
147
+ }
148
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["getTranslation","formatAdminURL","PREFERENCE_KEYS","React","formatRelativeDate","getRelativeTimeFormat","getPreferences","WidgetCard","WidgetList","displayLimit","RecentlyViewedWidget","req","widgetData","i18n","user","title","t","enrichedItems","getEnrichedItems","relativeTimeFormat","language","items","map","item","href","key","collectionSlug","id","main","_jsxs","_Fragment","thumbnailURL","_jsx","alt","className","src","typeLabel","meta","dateTime","viewedAt","value","emptyMessage","payload","excludedCollections","Set","preference","RECENTLY_VIEWED","collection","filter","has","Boolean","collections","length","idsByCollection","Map","ids","get","push","set","docsByKey","Promise","all","entries","result","find","depth","limit","overrideAccess","pagination","where","in","doc","docs","err","logger","error","msg","collectionConfig","config","useAsTitle","admin","rawTitle","getValueByPath","object","path","adminRoute","routes","upload","undefined","String","labels","singular","split","reduce","segment"],"sources":["../../../src/widgets/RecentlyViewed/index.tsx"],"sourcesContent":["import type { RecentlyViewedPreferences, WidgetServerProps } from 'payload'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport { formatAdminURL, PREFERENCE_KEYS } from 'payload/shared'\nimport React from 'react'\n\nimport type { WidgetListItem } from '../WidgetCard/index.js'\n\nimport { formatRelativeDate, getRelativeTimeFormat } from '../../utilities/formatRelativeDate.js'\nimport { getPreferences } from '../../utilities/upsertPreferences.js'\nimport { WidgetCard, WidgetList } from '../WidgetCard/index.js'\n\ntype RecentlyViewedWidgetData = {\n excludedCollections?: string[]\n}\n\ntype QueryDoc = {\n id: number | string\n thumbnailURL?: unknown\n} & Record<string, unknown>\n\ntype EnrichedItem = {\n collectionSlug: string\n href: string\n id: number | string\n thumbnailURL?: string\n title: string\n typeLabel: string\n viewedAt: string\n}\n\nconst displayLimit = 10\n\nexport async function RecentlyViewedWidget({\n req,\n widgetData,\n}: WidgetServerProps<{ data?: RecentlyViewedWidgetData }>) {\n const { i18n, user } = req\n const title = i18n.t('dashboard:widgetRecentlyViewedTitle')\n\n const enrichedItems = user ? await getEnrichedItems({ req, widgetData }) : []\n const relativeTimeFormat = getRelativeTimeFormat(i18n.language)\n\n const items: WidgetListItem[] = enrichedItems.map((item) => ({\n href: item.href,\n key: `${item.collectionSlug}:${item.id}`,\n main: (\n <>\n {item.thumbnailURL ? (\n <img alt=\"\" className=\"widget-card__thumbnail\" src={item.thumbnailURL} />\n ) : (\n <span className=\"widget-card__type\">{item.typeLabel}</span>\n )}\n <span className=\"widget-card__row-title\">{item.title}</span>\n </>\n ),\n meta: (\n <time dateTime={item.viewedAt}>\n {formatRelativeDate({ relativeTimeFormat, value: item.viewedAt })}\n </time>\n ),\n }))\n\n return (\n <WidgetCard className=\"recently-viewed-widget\" title={title}>\n <WidgetList emptyMessage={i18n.t('dashboard:widgetRecentlyViewedEmpty')} items={items} />\n </WidgetCard>\n )\n}\n\n/**\n * Loads the stored recently viewed identities and computes display fields from the live documents.\n * Documents are queried with access control, so deleted or now-forbidden docs are dropped.\n */\nasync function getEnrichedItems({\n req,\n widgetData,\n}: {\n req: WidgetServerProps['req']\n widgetData: RecentlyViewedWidgetData | undefined\n}): Promise<EnrichedItem[]> {\n const { i18n, payload, user } = req\n const excludedCollections = new Set(widgetData?.excludedCollections ?? [])\n\n const preference = await getPreferences<RecentlyViewedPreferences>(\n PREFERENCE_KEYS.RECENTLY_VIEWED,\n payload,\n user.id,\n user.collection,\n )\n\n const items = (preference?.value?.items ?? []).filter(\n (item) =>\n !excludedCollections.has(item.collectionSlug) &&\n Boolean(payload.collections[item.collectionSlug]),\n )\n\n if (items.length === 0) {\n return []\n }\n\n const idsByCollection = new Map<string, Array<number | string>>()\n for (const item of items) {\n const ids = idsByCollection.get(item.collectionSlug) ?? []\n ids.push(item.id)\n idsByCollection.set(item.collectionSlug, ids)\n }\n\n const docsByKey = new Map<string, QueryDoc>()\n await Promise.all(\n [...idsByCollection.entries()].map(async ([collectionSlug, ids]) => {\n try {\n const result = await payload.find({\n collection: collectionSlug,\n depth: 0,\n limit: ids.length,\n overrideAccess: false,\n pagination: false,\n user,\n where: { id: { in: ids } },\n })\n\n for (const doc of result.docs as QueryDoc[]) {\n docsByKey.set(`${collectionSlug}:${doc.id}`, doc)\n }\n } catch (err) {\n payload.logger.error({\n err,\n msg: `RecentlyViewedWidget: failed to load collection \"${collectionSlug}\"`,\n })\n }\n }),\n )\n\n const enrichedItems: EnrichedItem[] = []\n\n for (const item of items) {\n if (enrichedItems.length >= displayLimit) {\n break\n }\n\n const doc = docsByKey.get(`${item.collectionSlug}:${item.id}`)\n\n if (!doc) {\n continue\n }\n\n const collectionConfig = payload.collections[item.collectionSlug].config\n const useAsTitle = collectionConfig.admin?.useAsTitle || 'id'\n const rawTitle = getValueByPath({ object: doc, path: useAsTitle })\n\n enrichedItems.push({\n id: item.id,\n collectionSlug: item.collectionSlug,\n href: formatAdminURL({\n adminRoute: payload.config.routes.admin,\n path: `/collections/${item.collectionSlug}/${item.id}`,\n }),\n thumbnailURL:\n collectionConfig.upload && typeof doc.thumbnailURL === 'string'\n ? doc.thumbnailURL\n : undefined,\n title: typeof rawTitle === 'string' && rawTitle ? rawTitle : String(item.id),\n typeLabel: getTranslation(collectionConfig.labels.singular, i18n),\n viewedAt: item.viewedAt,\n })\n }\n\n return enrichedItems\n}\n\nfunction getValueByPath({ object, path }: { object: Record<string, unknown>; path: string }) {\n return path.split('.').reduce<unknown>((value, segment) => {\n if (!value || typeof value !== 'object') {\n return undefined\n }\n\n return (value as Record<string, unknown>)[segment]\n }, object)\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,155 @@
1
+ @layer payload-default {
2
+ .widget-card {
3
+ --widget-card-visible-rows: 5;
4
+ --widget-card-row-gap: var(--spacer-1);
5
+ --widget-card-row-height: 40px;
6
+ --widget-card-scrollbar-width: 6px;
7
+ --widget-card-rows-height: calc(
8
+ (var(--widget-card-row-height) + var(--widget-card-row-gap)) *
9
+ var(--widget-card-visible-rows) - var(--widget-card-row-gap)
10
+ );
11
+
12
+ flex-direction: column;
13
+ align-items: stretch;
14
+ gap: var(--spacer-2-5);
15
+ height: 100%;
16
+ /* Border + padding + header + header/rows gap + the visible row viewport. */
17
+ max-height: calc(
18
+ (var(--stroke-width-small) * 2) + (var(--spacer-2-5) * 3) +
19
+ var(--text-heading-medium-line-height) + var(--widget-card-rows-height)
20
+ );
21
+ overflow: hidden;
22
+ padding: var(--spacer-2-5);
23
+ }
24
+
25
+ .widget-card__header {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: var(--spacer-2);
29
+ }
30
+
31
+ .widget-card__title {
32
+ margin: 0;
33
+ color: var(--color-text);
34
+ font-family: var(--text-heading-medium-font-family);
35
+ font-size: var(--text-heading-medium-font-size);
36
+ font-weight: var(--text-heading-medium-font-weight);
37
+ line-height: var(--text-heading-medium-line-height);
38
+ letter-spacing: var(--text-heading-medium-letter-spacing);
39
+ }
40
+
41
+ .widget-card__rows {
42
+ display: flex;
43
+ flex: 1 1 auto;
44
+ flex-direction: column;
45
+ gap: var(--widget-card-row-gap);
46
+ min-height: 0;
47
+ overflow-y: auto;
48
+ padding: 0;
49
+ margin: 0;
50
+ list-style: none;
51
+ scrollbar-color: GrayText transparent;
52
+ scrollbar-width: thin;
53
+
54
+ &::-webkit-scrollbar {
55
+ width: var(--widget-card-scrollbar-width);
56
+ background: transparent;
57
+ }
58
+
59
+ &::-webkit-scrollbar-corner,
60
+ &::-webkit-scrollbar-track,
61
+ &::-webkit-scrollbar-track-piece {
62
+ background: transparent;
63
+ }
64
+
65
+ &::-webkit-scrollbar-thumb {
66
+ border-radius: var(--radius-small, 3px);
67
+ background: GrayText;
68
+ }
69
+ }
70
+
71
+ .widget-card__row {
72
+ margin: 0;
73
+ }
74
+
75
+ .widget-card__row-link {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: space-between;
79
+ gap: var(--spacer-2);
80
+ min-height: var(--widget-card-row-height);
81
+ padding: var(--spacer-1) calc(var(--spacer-1) * 1.5);
82
+ border: var(--stroke-width-small) solid var(--color-border);
83
+ border-radius: var(--radius-small, 3px);
84
+ background: var(--color-bg);
85
+ color: var(--color-text);
86
+ text-decoration: none;
87
+
88
+ &:hover {
89
+ background: var(--color-bg-secondary-hover);
90
+ text-decoration: none;
91
+ }
92
+ }
93
+
94
+ .widget-card__row-main,
95
+ .widget-card__row-meta {
96
+ display: inline-flex;
97
+ align-items: center;
98
+ min-width: 0;
99
+ }
100
+
101
+ .widget-card__row-main {
102
+ flex: 1 1 auto;
103
+ gap: calc(var(--spacer-1) * 1.5);
104
+ }
105
+
106
+ .widget-card__row-title {
107
+ overflow: hidden;
108
+ color: var(--color-text);
109
+ font-family: var(--text-body-medium-strong-font-family);
110
+ font-size: var(--text-body-medium-strong-font-size);
111
+ font-weight: var(--text-body-medium-strong-font-weight);
112
+ line-height: var(--text-body-medium-strong-line-height);
113
+ letter-spacing: var(--text-body-medium-strong-letter-spacing);
114
+ text-overflow: ellipsis;
115
+ white-space: nowrap;
116
+ }
117
+
118
+ .widget-card__row-meta {
119
+ flex: 0 0 auto;
120
+ gap: var(--spacer-1);
121
+ padding-left: calc(var(--spacer-1) * 1.5);
122
+ color: var(--color-text-secondary);
123
+ font-family: var(--text-body-medium-font-family);
124
+ font-size: var(--text-body-medium-font-size);
125
+ line-height: var(--text-body-medium-line-height);
126
+ letter-spacing: var(--text-body-medium-letter-spacing);
127
+ white-space: nowrap;
128
+ }
129
+
130
+ .widget-card__type {
131
+ flex: 0 0 auto;
132
+ padding: 0 var(--spacer-1);
133
+ border-radius: var(--radius-small, 3px);
134
+ background: var(--color-bg-secondary);
135
+ color: var(--color-text-secondary);
136
+ font-family: var(--text-body-medium-font-family);
137
+ font-size: var(--text-body-medium-font-size);
138
+ line-height: var(--text-body-medium-line-height);
139
+ letter-spacing: var(--text-body-medium-letter-spacing);
140
+ white-space: nowrap;
141
+ }
142
+
143
+ .widget-card__thumbnail {
144
+ width: 24px;
145
+ height: 24px;
146
+ flex: 0 0 auto;
147
+ object-fit: cover;
148
+ border-radius: var(--radius-small, 3px);
149
+ }
150
+
151
+ .widget-card__empty {
152
+ margin: 0;
153
+ color: var(--color-text-secondary);
154
+ }
155
+ }
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import '../../elements/Card/index.css';
3
+ import './index.css';
4
+ export type WidgetListItem = {
5
+ href: string;
6
+ key: string;
7
+ main: React.ReactNode;
8
+ meta?: React.ReactNode;
9
+ };
10
+ export declare function WidgetCard({ children, className, title, }: {
11
+ children: React.ReactNode;
12
+ className?: string;
13
+ title: React.ReactNode;
14
+ }): React.JSX.Element;
15
+ export declare function WidgetList({ emptyMessage, items, }: {
16
+ emptyMessage: string;
17
+ items: WidgetListItem[];
18
+ }): React.JSX.Element;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/widgets/WidgetCard/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,+BAA+B,CAAA;AACtC,OAAO,aAAa,CAAA;AAEpB,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,KAAK,CAAC,SAAS,CAAA;IACrB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACvB,CAAA;AAED,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,SAAS,EACT,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;CACvB,qBASA;AAED,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,KAAK,GACN,EAAE;IACD,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,cAAc,EAAE,CAAA;CACxB,qBAiBA"}
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import '../../elements/Card/index.css';
4
+ import './index.css';
5
+ export function WidgetCard({
6
+ children,
7
+ className,
8
+ title
9
+ }) {
10
+ return /*#__PURE__*/_jsxs("div", {
11
+ className: ['card', 'widget-card', className].filter(Boolean).join(' '),
12
+ children: [/*#__PURE__*/_jsx("div", {
13
+ className: "widget-card__header",
14
+ children: /*#__PURE__*/_jsx("h3", {
15
+ className: "widget-card__title",
16
+ children: title
17
+ })
18
+ }), children]
19
+ });
20
+ }
21
+ export function WidgetList({
22
+ emptyMessage,
23
+ items
24
+ }) {
25
+ if (items.length === 0) {
26
+ return /*#__PURE__*/_jsx("p", {
27
+ className: "widget-card__empty",
28
+ children: emptyMessage
29
+ });
30
+ }
31
+ return /*#__PURE__*/_jsx("ul", {
32
+ className: "widget-card__rows",
33
+ children: items.map(item => /*#__PURE__*/_jsx("li", {
34
+ className: "widget-card__row",
35
+ children: /*#__PURE__*/_jsxs("a", {
36
+ className: "widget-card__row-link",
37
+ href: item.href,
38
+ children: [/*#__PURE__*/_jsx("span", {
39
+ className: "widget-card__row-main",
40
+ children: item.main
41
+ }), item.meta != null ? /*#__PURE__*/_jsx("span", {
42
+ className: "widget-card__row-meta",
43
+ children: item.meta
44
+ }) : null]
45
+ })
46
+ }, item.key))
47
+ });
48
+ }
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["React","WidgetCard","children","className","title","_jsxs","filter","Boolean","join","_jsx","WidgetList","emptyMessage","items","length","map","item","href","main","meta","key"],"sources":["../../../src/widgets/WidgetCard/index.tsx"],"sourcesContent":["import React from 'react'\n\nimport '../../elements/Card/index.css'\nimport './index.css'\n\nexport type WidgetListItem = {\n href: string\n key: string\n main: React.ReactNode\n meta?: React.ReactNode\n}\n\nexport function WidgetCard({\n children,\n className,\n title,\n}: {\n children: React.ReactNode\n className?: string\n title: React.ReactNode\n}) {\n return (\n <div className={['card', 'widget-card', className].filter(Boolean).join(' ')}>\n <div className=\"widget-card__header\">\n <h3 className=\"widget-card__title\">{title}</h3>\n </div>\n {children}\n </div>\n )\n}\n\nexport function WidgetList({\n emptyMessage,\n items,\n}: {\n emptyMessage: string\n items: WidgetListItem[]\n}) {\n if (items.length === 0) {\n return <p className=\"widget-card__empty\">{emptyMessage}</p>\n }\n\n return (\n <ul className=\"widget-card__rows\">\n {items.map((item) => (\n <li className=\"widget-card__row\" key={item.key}>\n <a className=\"widget-card__row-link\" href={item.href}>\n <span className=\"widget-card__row-main\">{item.main}</span>\n {item.meta != null ? <span className=\"widget-card__row-meta\">{item.meta}</span> : null}\n </a>\n </li>\n ))}\n </ul>\n )\n}\n"],"mappings":";AAAA,OAAOA,KAAA,MAAW;AAElB,OAAO;AACP,OAAO;AASP,OAAO,SAASC,WAAW;EACzBC,QAAQ;EACRC,SAAS;EACTC;AAAK,CAKN;EACC,oBACEC,KAAA,CAAC;IAAIF,SAAA,EAAW,CAAC,QAAQ,eAAeA,SAAA,CAAU,CAACG,MAAM,CAACC,OAAA,EAASC,IAAI,CAAC;4BACtEC,IAAA,CAAC;MAAIN,SAAA,EAAU;gBACb,aAAAM,IAAA,CAAC;QAAGN,SAAA,EAAU;kBAAsBC;;QAErCF,QAAA;;AAGP;AAEA,OAAO,SAASQ,WAAW;EACzBC,YAAY;EACZC;AAAK,CAIN;EACC,IAAIA,KAAA,CAAMC,MAAM,KAAK,GAAG;IACtB,oBAAOJ,IAAA,CAAC;MAAEN,SAAA,EAAU;gBAAsBQ;;EAC5C;EAEA,oBACEF,IAAA,CAAC;IAAGN,SAAA,EAAU;cACXS,KAAA,CAAME,GAAG,CAAEC,IAAA,iBACVN,IAAA,CAAC;MAAGN,SAAA,EAAU;gBACZ,aAAAE,KAAA,CAAC;QAAEF,SAAA,EAAU;QAAwBa,IAAA,EAAMD,IAAA,CAAKC,IAAI;gCAClDP,IAAA,CAAC;UAAKN,SAAA,EAAU;oBAAyBY,IAAA,CAAKE;YAC7CF,IAAA,CAAKG,IAAI,IAAI,oBAAOT,IAAA,CAAC;UAAKN,SAAA,EAAU;oBAAyBY,IAAA,CAAKG;aAAe;;OAHhDH,IAAA,CAAKI,GAAG;;AAStD","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/ui",
3
- "version": "4.0.0-canary.6",
3
+ "version": "4.0.0-canary.7",
4
4
  "homepage": "https://payloadcms.com",
5
5
  "repository": {
6
6
  "type": "git",
@@ -243,7 +243,7 @@
243
243
  "ts-essentials": "10.0.3",
244
244
  "use-context-selector": "2.0.0",
245
245
  "uuid": "14.0.0",
246
- "@payloadcms/translations": "4.0.0-canary.6"
246
+ "@payloadcms/translations": "4.0.0-canary.7"
247
247
  },
248
248
  "devDependencies": {
249
249
  "@babel/cli": "7.27.2",
@@ -257,13 +257,13 @@
257
257
  "babel-plugin-react-compiler": "19.1.0-rc.3",
258
258
  "esbuild": "0.27.1",
259
259
  "esbuild-sass-plugin": "3.3.1",
260
- "payload": "4.0.0-canary.6",
261
- "@payloadcms/eslint-config": "3.28.0"
260
+ "@payloadcms/eslint-config": "3.28.0",
261
+ "payload": "4.0.0-canary.7"
262
262
  },
263
263
  "peerDependencies": {
264
264
  "react": "^19.0.1 || ^19.1.2 || ^19.2.1",
265
265
  "react-dom": "^19.0.1 || ^19.1.2 || ^19.2.1",
266
- "payload": "4.0.0-canary.6"
266
+ "payload": "4.0.0-canary.7"
267
267
  },
268
268
  "engines": {
269
269
  "node": ">=24.15.0"