@sanity/dashboard 3.1.5 → 4.0.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.
package/lib/index.mjs ADDED
@@ -0,0 +1,584 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { forwardRef, createContext, useContext, useMemo, createElement, useState, useEffect, useCallback } from "react";
3
+ import { Card, Box, Heading, Button, Stack, Label, Grid, Text, Code, Flex, rem, Spinner, Container } from "@sanity/ui";
4
+ import { styled, css } from "styled-components";
5
+ import { useClient, useListFormat, UserAvatar, useUserStore, definePlugin } from "sanity";
6
+ import { from } from "rxjs";
7
+ import { switchMap, map } from "rxjs/operators";
8
+ import { RobotIcon, PlayIcon } from "@sanity/icons";
9
+ import imageUrlBuilder from "@sanity/image-url";
10
+ const Root$3 = styled(Card)`
11
+ display: flex;
12
+ flex-direction: column;
13
+ justify-content: stretch;
14
+ height: 100%;
15
+ box-sizing: border-box;
16
+ position: relative;
17
+ `, Header = styled(Card)`
18
+ position: sticky;
19
+ top: 0;
20
+ z-index: 2;
21
+ border-top-left-radius: inherit;
22
+ border-top-right-radius: inherit;
23
+ `, Footer = styled(Card)`
24
+ position: sticky;
25
+ overflow: hidden;
26
+ bottom: 0;
27
+ z-index: 2;
28
+ border-bottom-right-radius: inherit;
29
+ border-bottom-left-radius: inherit;
30
+ margin-top: auto;
31
+ `, Content = styled(Box)`
32
+ position: relative;
33
+ z-index: 1;
34
+ height: stretch;
35
+ min-height: 21.5em;
36
+
37
+ @media (min-width: ${({ theme }) => theme.sanity.media[0]}px) {
38
+ overflow-y: auto;
39
+ outline: none;
40
+ }
41
+ `, DashboardWidgetContainer = forwardRef(function(props, ref) {
42
+ const { header, children, footer } = props;
43
+ return /* @__PURE__ */ jsxs(Root$3, { radius: 3, display: "flex", ref, children: [
44
+ header && /* @__PURE__ */ jsx(Header, { borderBottom: !0, paddingX: 3, paddingY: 4, children: /* @__PURE__ */ jsx(Heading, { size: 1, textOverflow: "ellipsis", children: header }) }),
45
+ children && /* @__PURE__ */ jsx(Content, { children }),
46
+ footer && /* @__PURE__ */ jsx(Footer, { borderTop: !0, children: footer })
47
+ ] });
48
+ });
49
+ function useVersionedClient() {
50
+ return useClient({ apiVersion: "2024-06-03" });
51
+ }
52
+ const DashboardContext = createContext({ widgets: [] });
53
+ function useDashboardConfig() {
54
+ return useContext(DashboardContext);
55
+ }
56
+ function WidgetContainer(props) {
57
+ const config = useDashboardConfig(), layout = useMemo(
58
+ () => ({
59
+ ...props.layout || {},
60
+ ...config.layout || {}
61
+ }),
62
+ [props.layout, config.layout]
63
+ );
64
+ return /* @__PURE__ */ jsx(Card, { shadow: 1, "data-width": layout.width, "data-height": layout.height, children: createElement(props.component, {}) });
65
+ }
66
+ function isUrl(url) {
67
+ return url && /^https?:\/\//.test(`${url}`);
68
+ }
69
+ function getGraphQlUrl(projectId, dataset) {
70
+ return `https://${projectId}.api.sanity.io/v1/graphql/${dataset}/default`;
71
+ }
72
+ function getGroqUrl(projectId, dataset) {
73
+ return `https://${projectId}.api.sanity.io/v1/groq/${dataset}`;
74
+ }
75
+ function getManageUrl(projectId) {
76
+ return `https://manage.sanity.io/projects/${projectId}`;
77
+ }
78
+ const NO_EXPERIMENTAL = [], NO_DATA = [];
79
+ function ProjectInfo(props) {
80
+ const { __experimental_before = NO_EXPERIMENTAL, data = NO_DATA } = props, [studioHost, setStudioHost] = useState(), [graphqlApi, setGraphQlApi] = useState(), versionedClient = useVersionedClient(), { projectId = "unknown", dataset = "unknown" } = versionedClient.config();
81
+ useEffect(() => {
82
+ const subscriptions = [];
83
+ return subscriptions.push(
84
+ versionedClient.observable.request({ uri: `/projects/${projectId}`, tag: "dashboard.project-info.studio-host" }).subscribe({
85
+ next: (result) => {
86
+ var _a;
87
+ if ((_a = result.metadata) != null && _a.externalStudioHost) {
88
+ setStudioHost(result.metadata.externalStudioHost);
89
+ return;
90
+ }
91
+ setStudioHost(
92
+ result.studioHost ? `https://${result.studioHost}.sanity.studio` : void 0
93
+ );
94
+ },
95
+ error: (error) => {
96
+ console.error("Error while looking for studioHost", error), setStudioHost({
97
+ error: "Something went wrong while looking up studioHost. See console."
98
+ });
99
+ }
100
+ })
101
+ ), subscriptions.push(
102
+ versionedClient.observable.request({
103
+ method: "HEAD",
104
+ uri: `/graphql/${dataset}/default`,
105
+ tag: "dashboard.project-info.graphql-api"
106
+ }).subscribe({
107
+ next: () => setGraphQlApi(getGraphQlUrl(projectId, dataset)),
108
+ error: (error) => {
109
+ error.statusCode === 404 ? setGraphQlApi(void 0) : (console.error("Error while looking for graphqlApi", error), setGraphQlApi({
110
+ error: "Something went wrong while looking up graphqlApi. See console."
111
+ }));
112
+ }
113
+ })
114
+ ), () => {
115
+ subscriptions.forEach((s) => s.unsubscribe());
116
+ };
117
+ }, [dataset, projectId, versionedClient, setGraphQlApi, setStudioHost]);
118
+ const assembleTableRows = useMemo(() => {
119
+ var _a;
120
+ let result = [
121
+ {
122
+ title: "Sanity project",
123
+ rows: [
124
+ { title: "Project ID", value: projectId },
125
+ { title: "Dataset", value: dataset }
126
+ ]
127
+ }
128
+ ];
129
+ const apps = [
130
+ studioHost ? { title: "Studio", value: studioHost } : null,
131
+ ...data.filter((item) => item.category === "apps")
132
+ ].filter((a) => !!a);
133
+ apps.length > 0 && (result = result.concat([{ title: "Apps", rows: apps }])), result = result.concat(
134
+ [
135
+ {
136
+ title: "APIs",
137
+ rows: [
138
+ { title: "GROQ", value: getGroqUrl(projectId, dataset) },
139
+ {
140
+ title: "GraphQL",
141
+ value: (_a = typeof graphqlApi == "object" ? "Error" : graphqlApi) != null ? _a : "Not deployed"
142
+ }
143
+ ]
144
+ }
145
+ ],
146
+ data.filter((item) => item.category === "apis")
147
+ );
148
+ const otherStuff = {};
149
+ return data.forEach((item) => {
150
+ item.category && item.category !== "apps" && item.category !== "apis" && (otherStuff[item.category] || (otherStuff[item.category] = []), otherStuff[item.category].push(item));
151
+ }), Object.keys(otherStuff).forEach((category) => {
152
+ result.push({ title: category, rows: otherStuff[category] });
153
+ }), result;
154
+ }, [graphqlApi, studioHost, projectId, dataset, data]);
155
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
156
+ __experimental_before.map((widgetConfig, idx) => /* @__PURE__ */ jsx(WidgetContainer, { ...widgetConfig }, idx)),
157
+ /* @__PURE__ */ jsx(Box, { height: "fill", marginTop: (__experimental_before == null ? void 0 : __experimental_before.length) > 0 ? 4 : 0, children: /* @__PURE__ */ jsx(
158
+ DashboardWidgetContainer,
159
+ {
160
+ footer: /* @__PURE__ */ jsx(
161
+ Button,
162
+ {
163
+ style: { width: "100%" },
164
+ paddingX: 2,
165
+ paddingY: 4,
166
+ mode: "bleed",
167
+ tone: "primary",
168
+ text: "Manage project",
169
+ as: "a",
170
+ href: getManageUrl(projectId)
171
+ }
172
+ ),
173
+ children: /* @__PURE__ */ jsx(
174
+ Card,
175
+ {
176
+ paddingY: 4,
177
+ radius: 2,
178
+ role: "table",
179
+ "aria-label": "Project info",
180
+ "aria-describedby": "project_info_table",
181
+ children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
182
+ /* @__PURE__ */ jsx(Box, { paddingX: 3, as: "header", children: /* @__PURE__ */ jsx(Heading, { size: 1, as: "h2", id: "project_info_table", children: "Project info" }) }),
183
+ assembleTableRows.map((item) => !item || !item.rows ? null : /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
184
+ /* @__PURE__ */ jsx(Card, { borderBottom: !0, padding: 3, children: /* @__PURE__ */ jsx(Label, { size: 0, muted: !0, role: "columnheader", children: item.title }) }),
185
+ /* @__PURE__ */ jsx(Stack, { space: 4, paddingX: 3, role: "rowgroup", children: item.rows.map((row) => {
186
+ var _a;
187
+ return /* @__PURE__ */ jsxs(Grid, { columns: 2, role: "row", children: [
188
+ /* @__PURE__ */ jsx(Text, { weight: "medium", role: "rowheader", children: row.title }),
189
+ typeof row.value == "object" && /* @__PURE__ */ jsx(Text, { size: 1, children: (_a = row.value) == null ? void 0 : _a.error }),
190
+ typeof row.value == "string" && /* @__PURE__ */ jsx(Fragment, { children: isUrl(row.value) ? /* @__PURE__ */ jsx(Text, { size: 1, role: "cell", style: { wordBreak: "break-word" }, children: /* @__PURE__ */ jsx("a", { href: row.value, children: row.value }) }) : /* @__PURE__ */ jsx(Code, { size: 1, role: "cell", style: { wordBreak: "break-word" }, children: row.value }) })
191
+ ] }, row.title);
192
+ }) })
193
+ ] }, item.title))
194
+ ] })
195
+ }
196
+ )
197
+ }
198
+ ) })
199
+ ] });
200
+ }
201
+ function projectInfoWidget(config) {
202
+ var _a;
203
+ return {
204
+ name: "project-info",
205
+ component: ProjectInfo,
206
+ layout: (_a = config == null ? void 0 : config.layout) != null ? _a : { width: "medium" }
207
+ };
208
+ }
209
+ const Root$2 = styled(Flex)`
210
+ height: ${rem(33)}; // 33 = PREVIEW_SIZES.default.media.height
211
+ box-sizing: content-box;
212
+ `;
213
+ function ProjectUser({ user, isRobot, roles }) {
214
+ const listFormat = useListFormat({ style: "narrow" });
215
+ return /* @__PURE__ */ jsx(Root$2, { align: "center", children: /* @__PURE__ */ jsxs(Flex, { align: "center", flex: 1, gap: 2, children: [
216
+ /* @__PURE__ */ jsx(Box, { flex: "none", children: isRobot ? /* @__PURE__ */ jsx(Text, { size: 2, children: /* @__PURE__ */ jsx(RobotIcon, {}) }) : /* @__PURE__ */ jsx(UserAvatar, { user }) }),
217
+ /* @__PURE__ */ jsxs(Stack, { flex: 1, space: 2, children: [
218
+ /* @__PURE__ */ jsx(Text, { size: 1, style: { color: "inherit" }, textOverflow: "ellipsis", weight: "medium", children: user.displayName }),
219
+ /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, textOverflow: "ellipsis", children: listFormat.format(roles) })
220
+ ] })
221
+ ] }) });
222
+ }
223
+ function getInviteUrl(projectId) {
224
+ return `https://manage.sanity.io/projects/${projectId}/members`;
225
+ }
226
+ function ProjectUsers() {
227
+ const [project, setProject] = useState(), [users, setUsers] = useState(), [error, setError] = useState(), userStore = useUserStore(), versionedClient = useVersionedClient(), fetchData = useCallback(() => {
228
+ const { projectId } = versionedClient.config(), subscription = versionedClient.observable.request({
229
+ uri: `/projects/${projectId}`,
230
+ tag: "dashboard.project-users"
231
+ }).pipe(
232
+ switchMap(
233
+ (_project) => from(userStore.getUsers(_project.members.map((mem) => mem.id))).pipe(
234
+ map((_users) => ({ project: _project, users: _users }))
235
+ )
236
+ )
237
+ ).subscribe({
238
+ next: ({ users: _users, project: _project }) => {
239
+ setProject(_project), setUsers(
240
+ (Array.isArray(_users) ? _users : [_users]).sort(
241
+ (userA, userB) => sortUsersByRobotStatus(userA, userB, _project)
242
+ )
243
+ );
244
+ },
245
+ error: (e) => setError(e)
246
+ });
247
+ return () => subscription.unsubscribe();
248
+ }, [userStore, versionedClient]);
249
+ useEffect(() => fetchData(), [fetchData]);
250
+ const handleRetryFetch = useCallback(() => fetchData(), [fetchData]), isLoading = !users || !project;
251
+ return error ? /* @__PURE__ */ jsx(DashboardWidgetContainer, { header: "Project users", children: /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsxs(Text, { children: [
252
+ "Something went wrong while fetching data. You could",
253
+ " ",
254
+ /* @__PURE__ */ jsx("a", { onClick: handleRetryFetch, title: "Retry users fetch", style: { cursor: "pointer" }, children: "retry" }),
255
+ "..?"
256
+ ] }) }) }) : /* @__PURE__ */ jsxs(
257
+ DashboardWidgetContainer,
258
+ {
259
+ header: "Project users",
260
+ footer: /* @__PURE__ */ jsx(
261
+ Button,
262
+ {
263
+ style: { width: "100%" },
264
+ paddingX: 2,
265
+ paddingY: 4,
266
+ mode: "bleed",
267
+ tone: "primary",
268
+ text: "Manage members",
269
+ as: "a",
270
+ loading: isLoading,
271
+ href: isLoading ? void 0 : getInviteUrl(project.id)
272
+ }
273
+ ),
274
+ children: [
275
+ isLoading && /* @__PURE__ */ jsx(Box, { paddingY: 5, paddingX: 2, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [
276
+ /* @__PURE__ */ jsx(Text, { align: "center", muted: !0, size: 1, children: /* @__PURE__ */ jsx(Spinner, {}) }),
277
+ /* @__PURE__ */ jsx(Text, { align: "center", size: 1, muted: !0, children: "Loading items\u2026" })
278
+ ] }) }),
279
+ !isLoading && /* @__PURE__ */ jsx(Stack, { space: 3, padding: 3, children: users == null ? void 0 : users.map((user) => {
280
+ var _a;
281
+ const membership = project.members.find((member) => member.id === user.id);
282
+ return /* @__PURE__ */ jsx(
283
+ ProjectUser,
284
+ {
285
+ user,
286
+ isRobot: (_a = membership == null ? void 0 : membership.isRobot) != null ? _a : !1,
287
+ roles: (membership == null ? void 0 : membership.roles.map((role) => role.title)) || []
288
+ },
289
+ user.id
290
+ );
291
+ }) })
292
+ ]
293
+ }
294
+ );
295
+ }
296
+ function sortUsersByRobotStatus(userA, userB, project) {
297
+ const { members } = project, membershipA = members.find((member) => member.id === (userA == null ? void 0 : userA.id)), membershipB = members.find((member) => member.id === (userB == null ? void 0 : userB.id));
298
+ return (membershipA == null ? void 0 : membershipA.isRobot) === (membershipB == null ? void 0 : membershipB.isRobot) ? ((membershipA == null ? void 0 : membershipA.createdAt) || "") > ((membershipB == null ? void 0 : membershipB.createdAt) || "") ? 1 : -1 : membershipA != null && membershipA.isRobot ? 1 : -1;
299
+ }
300
+ function projectUsersWidget(config) {
301
+ return {
302
+ name: "project-info",
303
+ component: ProjectUsers,
304
+ layout: config == null ? void 0 : config.layout
305
+ };
306
+ }
307
+ const PlayIconBox = styled(Box)`
308
+ position: absolute;
309
+ top: 50%;
310
+ left: 50%;
311
+ transform: translate(-50%, -50%);
312
+
313
+ &:before {
314
+ content: '';
315
+ position: absolute;
316
+ top: 50%;
317
+ left: 50%;
318
+ transform: translate(-50%, -50%);
319
+ width: 2.75em;
320
+ height: 2.75em;
321
+ border-radius: 50%;
322
+ background: ${({ theme }) => theme.sanity.color.card.enabled.bg};
323
+ opacity: 0.75;
324
+ }
325
+ `, Root$1 = styled(Flex)`
326
+ &:hover {
327
+ ${PlayIconBox} {
328
+ &:before {
329
+ opacity: 1;
330
+ }
331
+ }
332
+ }
333
+ `, PosterCard = styled(Card)`
334
+ width: 100%;
335
+ padding-bottom: calc(9 / 16 * 100%);
336
+ position: relative;
337
+ `, Poster = styled.img`
338
+ position: absolute;
339
+ top: 0;
340
+ left: 0;
341
+ height: 100%;
342
+ width: 100%;
343
+ object-fit: cover;
344
+ display: block;
345
+
346
+ &:not([src]) {
347
+ display: none;
348
+ }
349
+ `;
350
+ function Tutorial(props) {
351
+ const { title, posterURL, showPlayIcon, href, presenterName, presenterSubtitle } = props;
352
+ return /* @__PURE__ */ jsx(Root$1, { flex: 1, children: /* @__PURE__ */ jsx(
353
+ Card,
354
+ {
355
+ sizing: "border",
356
+ flex: 1,
357
+ padding: 2,
358
+ radius: 2,
359
+ as: "a",
360
+ href,
361
+ target: "_blank",
362
+ rel: "noopener noreferrer",
363
+ style: { position: "relative" },
364
+ children: /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { height: "100%" }, children: [
365
+ posterURL && /* @__PURE__ */ jsxs(PosterCard, { marginBottom: 1, children: [
366
+ /* @__PURE__ */ jsx(Poster, { src: posterURL }),
367
+ showPlayIcon && /* @__PURE__ */ jsx(PlayIconBox, { display: "flex", children: /* @__PURE__ */ jsx(Text, { align: "center", children: /* @__PURE__ */ jsx(PlayIcon, {}) }) })
368
+ ] }),
369
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", justify: "space-between", paddingY: 2, flex: 1, children: [
370
+ /* @__PURE__ */ jsx(Heading, { as: "h3", size: 1, children: title }),
371
+ /* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsxs(Stack, { space: 2, flex: 1, children: [
372
+ /* @__PURE__ */ jsx(Text, { size: 1, children: presenterName }),
373
+ /* @__PURE__ */ jsx(Text, { size: 0, style: { opacity: 0.7 }, children: presenterSubtitle })
374
+ ] }) })
375
+ ] })
376
+ ] })
377
+ }
378
+ ) });
379
+ }
380
+ const tutorialsProjectConfig = {
381
+ projectId: "3do82whm",
382
+ dataset: "next"
383
+ };
384
+ function useDataAdapter() {
385
+ const versionedClient = useVersionedClient();
386
+ return useMemo(
387
+ () => ({
388
+ getFeed: (templateRepoId) => {
389
+ const uri = templateRepoId ? `/addons/dashboard?templateRepoId=${templateRepoId}` : "/addons/dashboard";
390
+ return versionedClient.observable.request({
391
+ uri,
392
+ tag: "dashboard.sanity-tutorials",
393
+ withCredentials: !1
394
+ });
395
+ },
396
+ urlBuilder: imageUrlBuilder(tutorialsProjectConfig)
397
+ }),
398
+ [versionedClient]
399
+ );
400
+ }
401
+ function createUrl(slug, type) {
402
+ return type === "tutorial" ? `https://www.sanity.io/docs/tutorials/${slug.current}` : type === "guide" ? `https://www.sanity.io/docs/guides/${slug.current}` : !1;
403
+ }
404
+ function SanityTutorials(props) {
405
+ const { templateRepoId } = props, [feedItems, setFeedItems] = useState([]), { getFeed, urlBuilder } = useDataAdapter();
406
+ return useEffect(() => {
407
+ const subscription = getFeed(templateRepoId).subscribe((response) => {
408
+ setFeedItems(response.items);
409
+ });
410
+ return () => {
411
+ subscription.unsubscribe();
412
+ };
413
+ }, [setFeedItems, getFeed, templateRepoId]), /* @__PURE__ */ jsx(DashboardWidgetContainer, { header: "Learn about Sanity", children: /* @__PURE__ */ jsx(Flex, { as: "ul", overflow: "auto", align: "stretch", paddingY: 2, children: feedItems == null ? void 0 : feedItems.map((feedItem, index) => {
414
+ var _a;
415
+ if (!feedItem.title || !feedItem.guideOrTutorial && !feedItem.externalLink)
416
+ return null;
417
+ const presenter = feedItem.presenter || ((_a = feedItem.guideOrTutorial) == null ? void 0 : _a.presenter) || {}, subtitle = feedItem.category, { guideOrTutorial = {} } = feedItem, href = (guideOrTutorial.slug ? createUrl(guideOrTutorial.slug, guideOrTutorial._type) : feedItem.externalLink) || feedItem.externalLink;
418
+ return /* @__PURE__ */ jsx(
419
+ Flex,
420
+ {
421
+ as: "li",
422
+ paddingRight: index < (feedItems == null ? void 0 : feedItems.length) - 1 ? 1 : 3,
423
+ paddingLeft: index === 0 ? 3 : 0,
424
+ align: "stretch",
425
+ style: { minWidth: 272, width: "30%" },
426
+ children: /* @__PURE__ */ jsx(
427
+ Tutorial,
428
+ {
429
+ title: feedItem.title,
430
+ href: href != null ? href : "",
431
+ presenterName: presenter.name,
432
+ presenterSubtitle: subtitle,
433
+ showPlayIcon: feedItem.hasVideo,
434
+ posterURL: feedItem.poster ? urlBuilder.image(feedItem.poster).height(360).url() : void 0
435
+ }
436
+ )
437
+ },
438
+ feedItem._id
439
+ );
440
+ }) }) });
441
+ }
442
+ function sanityTutorialsWidget(config) {
443
+ var _a;
444
+ return {
445
+ name: "sanity-tutorials",
446
+ component: SanityTutorials,
447
+ layout: (_a = config == null ? void 0 : config.layout) != null ? _a : { width: "full" }
448
+ };
449
+ }
450
+ function DashboardLayout(props) {
451
+ return /* @__PURE__ */ jsx(Container, { width: 4, padding: 4, sizing: "border", style: { height: "100%", overflowY: "auto" }, children: props.children });
452
+ }
453
+ const media = {
454
+ small: (...args) => css`
455
+ @media (min-width: ${({ theme }) => theme.sanity.media[0]}px) {
456
+ ${css(...args)}
457
+ }
458
+ `,
459
+ medium: (...args) => css`
460
+ @media (min-width: ${({ theme }) => theme.sanity.media[2]}px) {
461
+ ${css(...args)}
462
+ }
463
+ `
464
+ }, Root = styled(Grid)`
465
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
466
+
467
+ & > div {
468
+ overflow: hidden;
469
+ }
470
+
471
+ & > div[data-width='medium'] {
472
+ ${media.small`
473
+ grid-column: span 2;
474
+ `}
475
+ }
476
+
477
+ & > div[data-width='large'] {
478
+ ${media.small`
479
+ grid-column: span 2;
480
+ `}
481
+
482
+ ${media.medium`
483
+ grid-column: span 3;
484
+ `}
485
+ }
486
+
487
+ & > div[data-width='full'] {
488
+ ${media.small`
489
+ grid-column: 1 / -1;
490
+ `}
491
+ }
492
+
493
+ & > div[data-height='medium'] {
494
+ ${media.small`
495
+ grid-row: span 2;
496
+ `}
497
+ }
498
+
499
+ & > div[data-height='large'] {
500
+ ${media.small`
501
+ grid-row: span 2;
502
+ `}
503
+
504
+ ${media.medium`
505
+ grid-row: span 3;
506
+ `}
507
+ }
508
+
509
+ & > div[data-height='full'] {
510
+ ${media.medium`
511
+ grid-row: 1 / -1;
512
+ `}
513
+ }
514
+ `, NO_WIDGETS = [], NO_LAYOUT = {};
515
+ function WidgetGroup(props) {
516
+ const {
517
+ config: { layout = NO_LAYOUT, widgets = NO_WIDGETS }
518
+ } = props;
519
+ return /* @__PURE__ */ jsxs(
520
+ Root,
521
+ {
522
+ autoFlow: "row dense",
523
+ "data-width": layout.width || "auto",
524
+ "data-height": layout.height || "auto",
525
+ gap: 4,
526
+ children: [
527
+ widgets.length ? null : /* @__PURE__ */ jsx(Card, { padding: 4, shadow: 1, tone: "primary", children: /* @__PURE__ */ jsx(Text, { align: "center", children: "Add some widgets to populate this space." }) }),
528
+ widgets.map((widgetConfig, index) => widgetConfig.type === "__experimental_group" ? /* @__PURE__ */ jsx(WidgetGroup, { config: widgetConfig }, index) : widgetConfig.component ? /* @__PURE__ */ jsx(WidgetContainer, { ...widgetConfig }, index) : /* @__PURE__ */ jsxs(Box, { children: [
529
+ widgetConfig.name,
530
+ " is missing widget component"
531
+ ] }, index))
532
+ ]
533
+ }
534
+ );
535
+ }
536
+ function Dashboard({ config }) {
537
+ return config ? /* @__PURE__ */ jsx(DashboardContext.Provider, { value: config, children: /* @__PURE__ */ jsx(DashboardLayout, { children: /* @__PURE__ */ jsx(WidgetGroup, { config }) }) }) : null;
538
+ }
539
+ const strokeStyle = {
540
+ stroke: "currentColor",
541
+ strokeWidth: 1.2
542
+ }, DashboardIcon = () => /* @__PURE__ */ jsxs(
543
+ "svg",
544
+ {
545
+ "data-sanity-icon": !0,
546
+ viewBox: "0 0 25 25",
547
+ fill: "none",
548
+ xmlns: "http://www.w3.org/2000/svg",
549
+ preserveAspectRatio: "xMidYMid",
550
+ width: "1em",
551
+ height: "1em",
552
+ children: [
553
+ /* @__PURE__ */ jsx("path", { d: "M19.5 19.5H5.5V5.5H19.5V19.5Z", style: strokeStyle }),
554
+ /* @__PURE__ */ jsx("path", { d: "M5.5 12.5H19.5", style: strokeStyle }),
555
+ /* @__PURE__ */ jsx("path", { d: "M14.5 19.5V12.5M10.5 12.5V5.5", style: strokeStyle })
556
+ ]
557
+ }
558
+ ), dashboardTool = definePlugin((config = {}) => {
559
+ var _a, _b, _c, _d, _e;
560
+ const pluginConfig = {
561
+ layout: (_a = config.defaultLayout) != null ? _a : {},
562
+ widgets: (_b = config.widgets) != null ? _b : []
563
+ }, title = (_c = config.title) != null ? _c : "Dashboard", name = (_d = config.name) != null ? _d : "dashboard", icon = (_e = config.icon) != null ? _e : DashboardIcon;
564
+ return {
565
+ name: "dashboard",
566
+ tools: (prev, context) => [
567
+ ...prev,
568
+ {
569
+ title,
570
+ name,
571
+ icon,
572
+ component: () => /* @__PURE__ */ jsx(Dashboard, { config: pluginConfig })
573
+ }
574
+ ]
575
+ };
576
+ });
577
+ export {
578
+ DashboardWidgetContainer,
579
+ dashboardTool,
580
+ projectInfoWidget,
581
+ projectUsersWidget,
582
+ sanityTutorialsWidget
583
+ };
584
+ //# sourceMappingURL=index.mjs.map