@panneau/app 5.0.0-alpha.4 → 5.0.0-alpha.6

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 (103) hide show
  1. package/README.md +33 -5
  2. package/dist/Panneau.css +19 -0
  3. package/dist/Panneau.d.ts +13 -1
  4. package/dist/Panneau.d.ts.map +1 -1
  5. package/dist/Panneau.js +144 -31
  6. package/dist/Panneau.js.map +1 -1
  7. package/dist/Panneau.module.js +12 -0
  8. package/dist/Panneau.module.js.map +1 -0
  9. package/dist/Shell.d.ts +8 -3
  10. package/dist/Shell.d.ts.map +1 -1
  11. package/dist/Shell.js +272 -114
  12. package/dist/Shell.js.map +1 -1
  13. package/dist/createPanneauApp.d.ts +9 -0
  14. package/dist/createPanneauApp.d.ts.map +1 -1
  15. package/dist/createPanneauApp.js.map +1 -1
  16. package/dist/cx.d.ts +3 -0
  17. package/dist/cx.d.ts.map +1 -0
  18. package/dist/cx.js +9 -0
  19. package/dist/cx.js.map +1 -0
  20. package/dist/documentTitle.d.ts +10 -0
  21. package/dist/documentTitle.d.ts.map +1 -0
  22. package/dist/documentTitle.js +49 -0
  23. package/dist/documentTitle.js.map +1 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -1
  27. package/dist/location.d.ts +13 -1
  28. package/dist/location.d.ts.map +1 -1
  29. package/dist/location.js +55 -48
  30. package/dist/location.js.map +1 -1
  31. package/dist/navigation.d.ts +53 -0
  32. package/dist/navigation.d.ts.map +1 -0
  33. package/dist/navigation.js +117 -0
  34. package/dist/navigation.js.map +1 -0
  35. package/dist/pages/HomePage.d.ts +11 -0
  36. package/dist/pages/HomePage.d.ts.map +1 -0
  37. package/dist/pages/HomePage.js +111 -0
  38. package/dist/pages/HomePage.js.map +1 -0
  39. package/dist/pages/ItemPanel.css +164 -0
  40. package/dist/pages/ItemPanel.d.ts +17 -0
  41. package/dist/pages/ItemPanel.d.ts.map +1 -0
  42. package/dist/pages/ItemPanel.js +370 -0
  43. package/dist/pages/ItemPanel.js.map +1 -0
  44. package/dist/pages/ItemPanel.module.js +34 -0
  45. package/dist/pages/ItemPanel.module.js.map +1 -0
  46. package/dist/pages/ItemPreview.d.ts +15 -0
  47. package/dist/pages/ItemPreview.d.ts.map +1 -0
  48. package/dist/pages/ItemPreview.js +179 -0
  49. package/dist/pages/ItemPreview.js.map +1 -0
  50. package/dist/pages/NotFoundPage.d.ts +6 -0
  51. package/dist/pages/NotFoundPage.d.ts.map +1 -0
  52. package/dist/pages/NotFoundPage.js +93 -0
  53. package/dist/pages/NotFoundPage.js.map +1 -0
  54. package/dist/pages/PageHeader.d.ts +21 -0
  55. package/dist/pages/PageHeader.d.ts.map +1 -0
  56. package/dist/pages/PageHeader.js +86 -0
  57. package/dist/pages/PageHeader.js.map +1 -0
  58. package/dist/pages/ResourceFormPage.d.ts +15 -0
  59. package/dist/pages/ResourceFormPage.d.ts.map +1 -0
  60. package/dist/pages/ResourceFormPage.js +204 -0
  61. package/dist/pages/ResourceFormPage.js.map +1 -0
  62. package/dist/pages/ResourceListPage.d.ts +15 -0
  63. package/dist/pages/ResourceListPage.d.ts.map +1 -0
  64. package/dist/pages/ResourceListPage.js +101 -0
  65. package/dist/pages/ResourceListPage.js.map +1 -0
  66. package/dist/pages/itemTitle.d.ts +7 -0
  67. package/dist/pages/itemTitle.d.ts.map +1 -0
  68. package/dist/pages/itemTitle.js +18 -0
  69. package/dist/pages/itemTitle.js.map +1 -0
  70. package/dist/pages/pages.css +162 -0
  71. package/dist/pages/pages.module.js +40 -0
  72. package/dist/pages/pages.module.js.map +1 -0
  73. package/dist/preferences.d.ts +6 -0
  74. package/dist/preferences.d.ts.map +1 -0
  75. package/dist/preferences.js +53 -0
  76. package/dist/preferences.js.map +1 -0
  77. package/dist/recentItems.d.ts +15 -0
  78. package/dist/recentItems.d.ts.map +1 -0
  79. package/dist/recentItems.js +70 -0
  80. package/dist/recentItems.js.map +1 -0
  81. package/dist/shell/AppLayout.css +189 -0
  82. package/dist/shell/AppLayout.d.ts +22 -0
  83. package/dist/shell/AppLayout.d.ts.map +1 -0
  84. package/dist/shell/AppLayout.js +365 -0
  85. package/dist/shell/AppLayout.js.map +1 -0
  86. package/dist/shell/AppLayout.module.js +36 -0
  87. package/dist/shell/AppLayout.module.js.map +1 -0
  88. package/dist/shell/GoTo.d.ts +20 -0
  89. package/dist/shell/GoTo.d.ts.map +1 -0
  90. package/dist/shell/GoTo.js +370 -0
  91. package/dist/shell/GoTo.js.map +1 -0
  92. package/dist/shell/Sidebar.css +313 -0
  93. package/dist/shell/Sidebar.d.ts +29 -0
  94. package/dist/shell/Sidebar.d.ts.map +1 -0
  95. package/dist/shell/Sidebar.js +225 -0
  96. package/dist/shell/Sidebar.js.map +1 -0
  97. package/dist/shell/Sidebar.module.js +60 -0
  98. package/dist/shell/Sidebar.module.js.map +1 -0
  99. package/dist/shell/tokens.d.ts +23 -0
  100. package/dist/shell/tokens.d.ts.map +1 -0
  101. package/dist/shell/tokens.js +75 -0
  102. package/dist/shell/tokens.js.map +1 -0
  103. package/package.json +15 -6
@@ -0,0 +1,17 @@
1
+ import type { ResolvedResource } from '@panneau/core';
2
+ export interface ItemPanelProps {
3
+ resolved: ResolvedResource;
4
+ basePath: string;
5
+ locale: string;
6
+ id: string;
7
+ /** Where closing the panel goes: the list, with its query. */
8
+ closeHref: string;
9
+ }
10
+ /**
11
+ * The details of an item next to its list (the show route): its values with the displays of the
12
+ * lists, a link to edit it, and the `link` actions of the resource, such as its page on the site.
13
+ * A resource with a `features.preview` gets a preview tab first: a component of a plugin, or its
14
+ * page on the site. The panel can be widened, for previews, and remembers it. Escape closes it.
15
+ */
16
+ export declare function ItemPanel({ resolved, basePath, locale, id, closeHref }: ItemPanelProps): import("react").JSX.Element;
17
+ //# sourceMappingURL=ItemPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ItemPanel.d.ts","sourceRoot":"","sources":["../../src/pages/ItemPanel.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAiCtD,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,cAAc,+BAgJtF"}
@@ -0,0 +1,370 @@
1
+ import { navigate } from "../location.js";
2
+ import { routePath } from "../routes.js";
3
+ import { useRecordRecentItem } from "../recentItems.js";
4
+ import { getItemTitle } from "./itemTitle.js";
5
+ import { cx } from "../cx.js";
6
+ import { usePreference } from "../preferences.js";
7
+ import ItemPanel_module_default from "./ItemPanel.module.js";
8
+ import { ItemPreview } from "./ItemPreview.js";
9
+ import { useEffect, useRef } from "react";
10
+ import { c } from "react/compiler-runtime";
11
+ import { useResourceItem } from "@panneau/data/query";
12
+ import { appMessages } from "@panneau/intl";
13
+ import { Alert, Button, Heading, Icon, IconButton, Spinner, Tab, TabList, TabPanel, Tabs, usePanneauIntl } from "@panneau/ui";
14
+ import { Link } from "react-aria-components";
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ import { ExternalLink, Maximize2, Minimize2, Pencil, X } from "lucide-react";
17
+ import { ItemDetails, getActionHref, isAllowedOn } from "@panneau/lists";
18
+ //#region src/pages/ItemPanel.tsx
19
+ /** Fields that hold other fields, shown in the form rather than in the details. */
20
+ var NESTED_TYPES = /* @__PURE__ */ new Set(["group", "repeater"]);
21
+ var SIZES = ["normal", "wide"];
22
+ var TABS = ["preview", "details"];
23
+ /**
24
+ * The details of an item next to its list (the show route): its values with the displays of the
25
+ * lists, a link to edit it, and the `link` actions of the resource, such as its page on the site.
26
+ * A resource with a `features.preview` gets a preview tab first: a component of a plugin, or its
27
+ * page on the site. The panel can be widened, for previews, and remembers it. Escape closes it.
28
+ */
29
+ function ItemPanel(t0) {
30
+ const $ = c(86);
31
+ const { resolved, basePath, locale, id, closeHref } = t0;
32
+ const intl = usePanneauIntl();
33
+ const { resource } = resolved;
34
+ const { data: item, isError, refetch } = useResourceItem(resource.id, id);
35
+ const panel = useRef(null);
36
+ const [size, setSize] = usePreference("item-panel-size", SIZES);
37
+ const [tab, setTab] = usePreference("item-panel-tab", TABS);
38
+ const wide = size === "wide";
39
+ const hasPreview = (resource.features?.preview ?? null) !== null;
40
+ const singular = resource.label.singular;
41
+ let t1;
42
+ if ($[0] !== intl || $[1] !== singular) {
43
+ t1 = intl.formatMessage(appMessages.itemPanel, { resource: singular });
44
+ $[0] = intl;
45
+ $[1] = singular;
46
+ $[2] = t1;
47
+ } else t1 = $[2];
48
+ const label = t1;
49
+ let t2;
50
+ if ($[3] !== item || $[4] !== locale || $[5] !== resource.itemTitle) {
51
+ t2 = getItemTitle(item, resource.itemTitle, locale) ?? (item !== void 0 ? item.id : "");
52
+ $[3] = item;
53
+ $[4] = locale;
54
+ $[5] = resource.itemTitle;
55
+ $[6] = t2;
56
+ } else t2 = $[6];
57
+ const title = t2;
58
+ let t3;
59
+ if ($[7] !== id || $[8] !== item || $[9] !== resource.id || $[10] !== title) {
60
+ t3 = item !== void 0 ? {
61
+ resource: resource.id,
62
+ id,
63
+ title
64
+ } : null;
65
+ $[7] = id;
66
+ $[8] = item;
67
+ $[9] = resource.id;
68
+ $[10] = title;
69
+ $[11] = t3;
70
+ } else t3 = $[11];
71
+ useRecordRecentItem(basePath, t3);
72
+ let t4;
73
+ if ($[12] === Symbol.for("react.memo_cache_sentinel")) {
74
+ t4 = () => {
75
+ panel.current?.focus();
76
+ };
77
+ $[12] = t4;
78
+ } else t4 = $[12];
79
+ let t5;
80
+ if ($[13] !== id) {
81
+ t5 = [id];
82
+ $[13] = id;
83
+ $[14] = t5;
84
+ } else t5 = $[14];
85
+ useEffect(t4, t5);
86
+ let t6;
87
+ if ($[15] !== resource.fields) {
88
+ t6 = resource.fields.filter(_temp).map(_temp2);
89
+ $[15] = resource.fields;
90
+ $[16] = t6;
91
+ } else t6 = $[16];
92
+ const columns = t6;
93
+ let t7;
94
+ if ($[17] !== basePath || $[18] !== closeHref || $[19] !== columns || $[20] !== hasPreview || $[21] !== id || $[22] !== intl || $[23] !== isError || $[24] !== item || $[25] !== label || $[26] !== locale || $[27] !== refetch || $[28] !== resource || $[29] !== setSize || $[30] !== setTab || $[31] !== singular || $[32] !== tab || $[33] !== title || $[34] !== wide) {
95
+ let t8;
96
+ if ($[36] !== item) {
97
+ t8 = (action) => {
98
+ if (item === void 0 || !action.scope.includes("item") || !isAllowedOn(action, item)) return [];
99
+ const href = getActionHref(action, item);
100
+ return href !== null ? [{
101
+ action,
102
+ href
103
+ }] : [];
104
+ };
105
+ $[36] = item;
106
+ $[37] = t8;
107
+ } else t8 = $[37];
108
+ const links = (resource.actions ?? []).flatMap(t8);
109
+ let t9;
110
+ if ($[38] !== closeHref) {
111
+ t9 = (event) => {
112
+ if (event.key === "Escape" && !event.defaultPrevented) {
113
+ event.preventDefault();
114
+ navigate(closeHref);
115
+ }
116
+ };
117
+ $[38] = closeHref;
118
+ $[39] = t9;
119
+ } else t9 = $[39];
120
+ const onKeyDown = t9;
121
+ let content;
122
+ if (item === void 0) {
123
+ let t10;
124
+ if ($[40] !== intl || $[41] !== isError || $[42] !== refetch || $[43] !== singular) {
125
+ t10 = isError ? /* @__PURE__ */ jsxs(Alert, {
126
+ tone: "danger",
127
+ children: [/* @__PURE__ */ jsx("p", { children: intl.formatMessage(appMessages.itemLoadError, { resource: singular }) }), /* @__PURE__ */ jsx(Button, {
128
+ size: "sm",
129
+ onPress: () => void refetch(),
130
+ children: intl.formatMessage(appMessages.retry)
131
+ })]
132
+ }) : /* @__PURE__ */ jsx(Spinner, { showLabel: true });
133
+ $[40] = intl;
134
+ $[41] = isError;
135
+ $[42] = refetch;
136
+ $[43] = singular;
137
+ $[44] = t10;
138
+ } else t10 = $[44];
139
+ content = t10;
140
+ } else {
141
+ let t10;
142
+ if ($[45] !== columns || $[46] !== item) {
143
+ t10 = /* @__PURE__ */ jsx(ItemDetails, {
144
+ item,
145
+ columns
146
+ });
147
+ $[45] = columns;
148
+ $[46] = item;
149
+ $[47] = t10;
150
+ } else t10 = $[47];
151
+ const details = t10;
152
+ let t11;
153
+ if ($[48] !== details || $[49] !== hasPreview || $[50] !== intl || $[51] !== item || $[52] !== label || $[53] !== locale || $[54] !== resource || $[55] !== setTab || $[56] !== tab || $[57] !== title) {
154
+ t11 = hasPreview ? /* @__PURE__ */ jsxs(Tabs, {
155
+ selectedKey: tab ?? "preview",
156
+ onSelectionChange: (key) => setTab(key === "details" ? "details" : "preview"),
157
+ className: ItemPanel_module_default.tabs,
158
+ children: [
159
+ /* @__PURE__ */ jsxs(TabList, {
160
+ "aria-label": label,
161
+ children: [/* @__PURE__ */ jsx(Tab, {
162
+ id: "preview",
163
+ children: intl.formatMessage(appMessages.preview)
164
+ }), /* @__PURE__ */ jsx(Tab, {
165
+ id: "details",
166
+ children: intl.formatMessage(appMessages.details)
167
+ })]
168
+ }),
169
+ /* @__PURE__ */ jsx(TabPanel, {
170
+ id: "preview",
171
+ className: ItemPanel_module_default.tabPanel,
172
+ children: /* @__PURE__ */ jsx(ItemPreview, {
173
+ resource,
174
+ item,
175
+ title,
176
+ locale
177
+ })
178
+ }),
179
+ /* @__PURE__ */ jsx(TabPanel, {
180
+ id: "details",
181
+ className: ItemPanel_module_default.tabPanel,
182
+ children: details
183
+ })
184
+ ]
185
+ }) : details;
186
+ $[48] = details;
187
+ $[49] = hasPreview;
188
+ $[50] = intl;
189
+ $[51] = item;
190
+ $[52] = label;
191
+ $[53] = locale;
192
+ $[54] = resource;
193
+ $[55] = setTab;
194
+ $[56] = tab;
195
+ $[57] = title;
196
+ $[58] = t11;
197
+ } else t11 = $[58];
198
+ content = t11;
199
+ }
200
+ const t10 = wide && ItemPanel_module_default.wide;
201
+ let t11;
202
+ if ($[59] !== t10) {
203
+ t11 = cx(ItemPanel_module_default.panel, t10);
204
+ $[59] = t10;
205
+ $[60] = t11;
206
+ } else t11 = $[60];
207
+ let t12;
208
+ if ($[61] !== title) {
209
+ t12 = /* @__PURE__ */ jsx(Heading, {
210
+ level: 2,
211
+ className: ItemPanel_module_default.title,
212
+ children: title
213
+ });
214
+ $[61] = title;
215
+ $[62] = t12;
216
+ } else t12 = $[62];
217
+ const t13 = wide ? Minimize2 : Maximize2;
218
+ let t14;
219
+ if ($[63] !== intl || $[64] !== wide) {
220
+ t14 = intl.formatMessage(wide ? appMessages.narrowPanel : appMessages.widenPanel);
221
+ $[63] = intl;
222
+ $[64] = wide;
223
+ $[65] = t14;
224
+ } else t14 = $[65];
225
+ let t15;
226
+ if ($[66] !== setSize || $[67] !== wide) {
227
+ t15 = () => setSize(wide ? "normal" : "wide");
228
+ $[66] = setSize;
229
+ $[67] = wide;
230
+ $[68] = t15;
231
+ } else t15 = $[68];
232
+ let t16;
233
+ if ($[69] !== t13 || $[70] !== t14 || $[71] !== t15) {
234
+ t16 = /* @__PURE__ */ jsx(IconButton, {
235
+ icon: t13,
236
+ label: t14,
237
+ size: "sm",
238
+ className: ItemPanel_module_default.widen,
239
+ onPress: t15
240
+ });
241
+ $[69] = t13;
242
+ $[70] = t14;
243
+ $[71] = t15;
244
+ $[72] = t16;
245
+ } else t16 = $[72];
246
+ let t17;
247
+ if ($[73] !== intl) {
248
+ t17 = intl.formatMessage(appMessages.closePanel);
249
+ $[73] = intl;
250
+ $[74] = t17;
251
+ } else t17 = $[74];
252
+ let t18;
253
+ if ($[75] !== closeHref) {
254
+ t18 = () => navigate(closeHref);
255
+ $[75] = closeHref;
256
+ $[76] = t18;
257
+ } else t18 = $[76];
258
+ let t19;
259
+ if ($[77] !== t17 || $[78] !== t18) {
260
+ t19 = /* @__PURE__ */ jsx(IconButton, {
261
+ icon: X,
262
+ label: t17,
263
+ size: "sm",
264
+ onPress: t18
265
+ });
266
+ $[77] = t17;
267
+ $[78] = t18;
268
+ $[79] = t19;
269
+ } else t19 = $[79];
270
+ let t20;
271
+ if ($[80] !== t16 || $[81] !== t19) {
272
+ t20 = /* @__PURE__ */ jsxs("div", {
273
+ className: ItemPanel_module_default.headerButtons,
274
+ children: [t16, t19]
275
+ });
276
+ $[80] = t16;
277
+ $[81] = t19;
278
+ $[82] = t20;
279
+ } else t20 = $[82];
280
+ let t21;
281
+ if ($[83] !== t12 || $[84] !== t20) {
282
+ t21 = /* @__PURE__ */ jsxs("header", {
283
+ className: ItemPanel_module_default.header,
284
+ children: [t12, t20]
285
+ });
286
+ $[83] = t12;
287
+ $[84] = t20;
288
+ $[85] = t21;
289
+ } else t21 = $[85];
290
+ t7 = /* @__PURE__ */ jsxs("section", {
291
+ ref: panel,
292
+ tabIndex: -1,
293
+ "aria-label": label,
294
+ className: t11,
295
+ "data-wide": wide || void 0,
296
+ onKeyDown,
297
+ children: [
298
+ t21,
299
+ item !== void 0 ? /* @__PURE__ */ jsxs("div", {
300
+ className: ItemPanel_module_default.actions,
301
+ children: [item.abilities.update !== false ? /* @__PURE__ */ jsxs(Link, {
302
+ href: routePath({
303
+ name: "resource.edit",
304
+ resource: resource.id,
305
+ id
306
+ }, basePath),
307
+ className: ItemPanel_module_default.action,
308
+ children: [/* @__PURE__ */ jsx(Icon, {
309
+ icon: Pencil,
310
+ size: "sm"
311
+ }), intl.formatMessage(appMessages.editItem)]
312
+ }) : null, links.map(_temp3)]
313
+ }) : null,
314
+ /* @__PURE__ */ jsx("div", {
315
+ className: ItemPanel_module_default.body,
316
+ children: content
317
+ })
318
+ ]
319
+ });
320
+ $[17] = basePath;
321
+ $[18] = closeHref;
322
+ $[19] = columns;
323
+ $[20] = hasPreview;
324
+ $[21] = id;
325
+ $[22] = intl;
326
+ $[23] = isError;
327
+ $[24] = item;
328
+ $[25] = label;
329
+ $[26] = locale;
330
+ $[27] = refetch;
331
+ $[28] = resource;
332
+ $[29] = setSize;
333
+ $[30] = setTab;
334
+ $[31] = singular;
335
+ $[32] = tab;
336
+ $[33] = title;
337
+ $[34] = wide;
338
+ $[35] = t7;
339
+ } else t7 = $[35];
340
+ return t7;
341
+ }
342
+ function _temp3(t0) {
343
+ const { action: action_0, href: href_0 } = t0;
344
+ const external = /^https?:\/\//.test(href_0);
345
+ return /* @__PURE__ */ jsxs(Link, {
346
+ href: href_0,
347
+ target: external ? "_blank" : void 0,
348
+ rel: external ? "noopener noreferrer" : void 0,
349
+ className: ItemPanel_module_default.action,
350
+ children: [external ? /* @__PURE__ */ jsx(Icon, {
351
+ icon: ExternalLink,
352
+ size: "sm"
353
+ }) : null, action_0.label]
354
+ }, action_0.id);
355
+ }
356
+ function _temp2(field_0) {
357
+ return {
358
+ id: field_0.name,
359
+ field: field_0,
360
+ label: field_0.label ?? field_0.name,
361
+ display: field_0.display ?? field_0.type
362
+ };
363
+ }
364
+ function _temp(field) {
365
+ return !NESTED_TYPES.has(field.type);
366
+ }
367
+ //#endregion
368
+ export { ItemPanel };
369
+
370
+ //# sourceMappingURL=ItemPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ItemPanel.js","names":["content","item","columns","label","resource","title","locale","details","X","panel","onKeyDown","Pencil","href","action","ExternalLink","field","_c","styles"],"sources":["../../src/pages/ItemPanel.tsx"],"sourcesContent":["import { ExternalLink, Maximize2, Minimize2, Pencil, X } from 'lucide-react';\nimport { type KeyboardEvent, useEffect, useRef } from 'react';\nimport { Link } from 'react-aria-components';\n\nimport type { ResolvedResource } from '@panneau/core';\nimport { useResourceItem } from '@panneau/data/query';\nimport { appMessages } from '@panneau/intl';\nimport { ItemDetails, type ListColumn, getActionHref, isAllowedOn } from '@panneau/lists';\nimport {\n Alert,\n Button,\n Heading,\n Icon,\n IconButton,\n Spinner,\n Tab,\n TabList,\n TabPanel,\n Tabs,\n usePanneauIntl,\n} from '@panneau/ui';\n\nimport { cx } from '../cx';\nimport { navigate } from '../location';\nimport { usePreference } from '../preferences';\nimport { useRecordRecentItem } from '../recentItems';\nimport { routePath } from '../routes';\nimport styles from './ItemPanel.module.css';\nimport { ItemPreview } from './ItemPreview';\nimport { getItemTitle } from './itemTitle';\n\n/** Fields that hold other fields, shown in the form rather than in the details. */\nconst NESTED_TYPES = new Set(['group', 'repeater']);\n\nconst SIZES = ['normal', 'wide'] as const;\nconst TABS = ['preview', 'details'] as const;\n\nexport interface ItemPanelProps {\n resolved: ResolvedResource;\n basePath: string;\n locale: string;\n id: string;\n /** Where closing the panel goes: the list, with its query. */\n closeHref: string;\n}\n\n/**\n * The details of an item next to its list (the show route): its values with the displays of the\n * lists, a link to edit it, and the `link` actions of the resource, such as its page on the site.\n * A resource with a `features.preview` gets a preview tab first: a component of a plugin, or its\n * page on the site. The panel can be widened, for previews, and remembers it. Escape closes it.\n */\nexport function ItemPanel({ resolved, basePath, locale, id, closeHref }: ItemPanelProps) {\n const intl = usePanneauIntl();\n const { resource } = resolved;\n const { data: item, isError, refetch } = useResourceItem(resource.id, id);\n const panel = useRef<HTMLElement>(null);\n const [size, setSize] = usePreference('item-panel-size', SIZES);\n const [tab, setTab] = usePreference('item-panel-tab', TABS);\n const wide = size === 'wide';\n const hasPreview = (resource.features?.preview ?? null) !== null;\n const singular = resource.label.singular;\n const label = intl.formatMessage(appMessages.itemPanel, { resource: singular });\n const title =\n getItemTitle(item, resource.itemTitle, locale) ?? (item !== undefined ? item.id : '');\n useRecordRecentItem(basePath, item !== undefined ? { resource: resource.id, id, title } : null);\n\n // Focus moves to the panel when it opens or shows another item.\n useEffect(() => {\n panel.current?.focus();\n }, [id]);\n\n const columns: ListColumn[] = resource.fields\n .filter((field) => !NESTED_TYPES.has(field.type))\n .map((field) => ({\n id: field.name,\n field,\n label: field.label ?? field.name,\n display: field.display ?? field.type,\n }));\n const links = (resource.actions ?? []).flatMap((action) => {\n if (item === undefined || !action.scope.includes('item') || !isAllowedOn(action, item)) {\n return [];\n }\n const href = getActionHref(action, item);\n return href !== null ? [{ action, href }] : [];\n });\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !event.defaultPrevented) {\n event.preventDefault();\n navigate(closeHref);\n }\n };\n\n let content;\n if (item === undefined) {\n content = isError ? (\n <Alert tone=\"danger\">\n <p>{intl.formatMessage(appMessages.itemLoadError, { resource: singular })}</p>\n <Button size=\"sm\" onPress={() => void refetch()}>\n {intl.formatMessage(appMessages.retry)}\n </Button>\n </Alert>\n ) : (\n <Spinner showLabel />\n );\n } else {\n const details = <ItemDetails item={item} columns={columns} />;\n content = hasPreview ? (\n <Tabs\n selectedKey={tab ?? 'preview'}\n onSelectionChange={(key) => setTab(key === 'details' ? 'details' : 'preview')}\n className={styles.tabs}\n >\n <TabList aria-label={label}>\n <Tab id=\"preview\">{intl.formatMessage(appMessages.preview)}</Tab>\n <Tab id=\"details\">{intl.formatMessage(appMessages.details)}</Tab>\n </TabList>\n <TabPanel id=\"preview\" className={styles.tabPanel}>\n <ItemPreview resource={resource} item={item} title={title} locale={locale} />\n </TabPanel>\n <TabPanel id=\"details\" className={styles.tabPanel}>\n {details}\n </TabPanel>\n </Tabs>\n ) : (\n details\n );\n }\n\n return (\n <section\n ref={panel}\n tabIndex={-1}\n aria-label={label}\n className={cx(styles.panel, wide && styles.wide)}\n data-wide={wide || undefined}\n onKeyDown={onKeyDown}\n >\n <header className={styles.header}>\n <Heading level={2} className={styles.title}>\n {title}\n </Heading>\n <div className={styles.headerButtons}>\n <IconButton\n icon={wide ? Minimize2 : Maximize2}\n label={intl.formatMessage(\n wide ? appMessages.narrowPanel : appMessages.widenPanel,\n )}\n size=\"sm\"\n className={styles.widen}\n onPress={() => setSize(wide ? 'normal' : 'wide')}\n />\n <IconButton\n icon={X}\n label={intl.formatMessage(appMessages.closePanel)}\n size=\"sm\"\n onPress={() => navigate(closeHref)}\n />\n </div>\n </header>\n {item !== undefined ? (\n <div className={styles.actions}>\n {item.abilities.update !== false ? (\n <Link\n href={routePath(\n { name: 'resource.edit', resource: resource.id, id },\n basePath,\n )}\n className={styles.action}\n >\n <Icon icon={Pencil} size=\"sm\" />\n {intl.formatMessage(appMessages.editItem)}\n </Link>\n ) : null}\n {links.map(({ action, href }) => {\n const external = /^https?:\\/\\//.test(href);\n return (\n <Link\n key={action.id}\n href={href}\n target={external ? '_blank' : undefined}\n rel={external ? 'noopener noreferrer' : undefined}\n className={styles.action}\n >\n {external ? <Icon icon={ExternalLink} size=\"sm\" /> : null}\n {action.label}\n </Link>\n );\n })}\n </div>\n ) : null}\n <div className={styles.body}>{content}</div>\n </section>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import "./ItemPanel.css";
2
+ //#region src/pages/ItemPanel.module.css
3
+ var panel = "panneau-item-panel-panel";
4
+ var wide = "panneau-item-panel-wide";
5
+ var header = "panneau-item-panel-header";
6
+ var headerButtons = "panneau-item-panel-header-buttons";
7
+ var title = "panneau-item-panel-title";
8
+ var actions = "panneau-item-panel-actions";
9
+ var action = "panneau-item-panel-action";
10
+ var body = "panneau-item-panel-body";
11
+ var tabs = "panneau-item-panel-tabs";
12
+ var tabPanel = "panneau-item-panel-tab-panel";
13
+ var preview = "panneau-item-panel-preview";
14
+ var previewLink = "panneau-item-panel-preview-link";
15
+ var frame = "panneau-item-panel-frame";
16
+ var ItemPanel_module_default = {
17
+ panel,
18
+ wide,
19
+ header,
20
+ headerButtons,
21
+ title,
22
+ actions,
23
+ action,
24
+ body,
25
+ tabs,
26
+ tabPanel,
27
+ preview,
28
+ previewLink,
29
+ frame
30
+ };
31
+ //#endregion
32
+ export { action, actions, body, ItemPanel_module_default as default, frame, header, headerButtons, panel, preview, previewLink, tabPanel, tabs, title, wide };
33
+
34
+ //# sourceMappingURL=ItemPanel.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ItemPanel.module.js","names":[],"sources":["../../src/pages/ItemPanel.module.css"],"sourcesContent":["/* The details of an item next to its list. Beside the list on a wide screen, over it otherwise. */\n\n.panel {\n position: fixed;\n inset-block: 0;\n inset-inline-end: 0;\n z-index: var(--panneau-z-overlay);\n display: flex;\n flex-direction: column;\n width: min(100%, var(--panneau-item-panel-width, 24rem));\n background: var(--panneau-item-panel-bg, var(--panneau-color-surface));\n border-inline-start: var(--panneau-border-width) solid var(--panneau-color-border);\n box-shadow: var(--panneau-shadow-lg);\n outline: none;\n}\n\n/* Widened, for previews. */\n.wide {\n width: min(100%, var(--panneau-item-panel-width-wide, 50%));\n}\n\n.panel:focus-visible {\n box-shadow:\n var(--panneau-shadow-lg),\n inset 0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);\n}\n\n.header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--panneau-space-2);\n padding: var(--panneau-space-4) var(--panneau-space-4) var(--panneau-space-2);\n}\n\n.headerButtons {\n display: flex;\n flex: none;\n gap: var(--panneau-space-1);\n}\n\n.title {\n min-width: 0;\n margin: 0;\n overflow-wrap: anywhere;\n}\n\n.actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--panneau-space-2);\n padding: 0 var(--panneau-space-4) var(--panneau-space-3);\n border-bottom: var(--panneau-border-width) solid var(--panneau-color-border);\n}\n\n.action {\n display: inline-flex;\n align-items: center;\n gap: var(--panneau-space-1);\n min-height: var(--panneau-control-height-sm);\n padding: 0 var(--panneau-space-3);\n border: var(--panneau-border-width) solid var(--panneau-color-border-control);\n border-radius: var(--panneau-radius-md);\n color: var(--panneau-color-text);\n font-size: var(--panneau-font-size-sm);\n font-weight: var(--panneau-font-weight-medium);\n text-decoration: none;\n outline: none;\n}\n\n.action[data-hovered] {\n background: var(--panneau-color-hover);\n}\n\n.action[data-focus-visible] {\n box-shadow: 0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);\n}\n\n.body {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-height: 0;\n padding: var(--panneau-space-4);\n overflow-y: auto;\n}\n\n.tabs {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--panneau-space-3);\n min-height: 0;\n}\n\n.tabPanel {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-height: 0;\n}\n\n.preview {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: var(--panneau-space-2);\n min-height: 0;\n}\n\n.previewLink {\n display: inline-flex;\n align-items: center;\n align-self: flex-end;\n gap: var(--panneau-space-1);\n border-radius: var(--panneau-radius-sm);\n color: var(--panneau-color-link);\n font-size: var(--panneau-font-size-sm);\n text-decoration: none;\n outline: none;\n}\n\n.previewLink[data-hovered] {\n text-decoration: underline;\n}\n\n.previewLink[data-focus-visible] {\n box-shadow: 0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);\n}\n\n/* The page of the site, as tall as the panel allows. */\n.frame {\n flex: 1 1 auto;\n width: 100%;\n min-height: 28rem;\n border: var(--panneau-border-width) solid var(--panneau-color-border);\n border-radius: var(--panneau-radius-md);\n background: var(--panneau-color-surface);\n}\n\n/* From the `xl` width of the root: beside the list, in the flow of the page. */\n@container panneau-root (min-width: 80rem) {\n .panel {\n position: sticky;\n top: var(--panneau-space-4);\n z-index: auto;\n width: auto;\n max-height: calc(100dvh - 2 * var(--panneau-space-4));\n min-height: min(40rem, calc(100dvh - 2 * var(--panneau-space-4)));\n border: var(--panneau-border-width) solid var(--panneau-color-border);\n border-radius: var(--panneau-item-panel-radius, var(--panneau-radius-lg));\n box-shadow: var(--panneau-item-panel-shadow, var(--panneau-shadow-md));\n }\n\n .wide {\n width: auto;\n }\n\n .panel:focus-visible {\n box-shadow:\n var(--panneau-item-panel-shadow, var(--panneau-shadow-md)),\n 0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);\n }\n}\n"],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import type { Item, Resource } from '@panneau/protocol';
2
+ export interface ItemPreviewProps {
3
+ resource: Resource;
4
+ item: Item;
5
+ /** The title of the item, for the accessible name of the preview. */
6
+ title: string;
7
+ locale: string;
8
+ }
9
+ /**
10
+ * The preview of an item, as its resource's `features.preview` says (decision 0009): a component
11
+ * of a plugin (`previews`), or the item's page on the site in an iframe, from a short-lived URL
12
+ * of the backend.
13
+ */
14
+ export declare function ItemPreview({ resource, item, title, locale }: ItemPreviewProps): import("react").JSX.Element | null;
15
+ //# sourceMappingURL=ItemPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ItemPreview.d.ts","sourceRoot":"","sources":["../../src/pages/ItemPreview.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAKxD,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC;IACX,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAClB;AA4CD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,gBAAgB,sCAsB9E"}
@@ -0,0 +1,179 @@
1
+ import ItemPanel_module_default from "./ItemPanel.module.js";
2
+ import { createElement } from "react";
3
+ import { c } from "react/compiler-runtime";
4
+ import { usePlugins } from "@panneau/core";
5
+ import { useItemPreview } from "@panneau/data/query";
6
+ import { appMessages } from "@panneau/intl";
7
+ import { Alert, Button, Icon, Spinner, usePanneauIntl } from "@panneau/ui";
8
+ import { Link } from "react-aria-components";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { ExternalLink } from "lucide-react";
11
+ //#region src/pages/ItemPreview.tsx
12
+ /** Opens the URLs a new tab can: not the `data:` and `blob:` ones of tests and mocks. */
13
+ function canOpen(url) {
14
+ return /^(https?:)?\/\//.test(url) || url.startsWith("/");
15
+ }
16
+ function IframePreview(t0) {
17
+ const $ = c(16);
18
+ const { resource, item, title } = t0;
19
+ const intl = usePanneauIntl();
20
+ const { data, isError, refetch } = useItemPreview(resource.id, item.id);
21
+ if (data === void 0) {
22
+ let t1;
23
+ if ($[0] !== intl || $[1] !== isError || $[2] !== refetch) {
24
+ t1 = isError ? /* @__PURE__ */ jsxs(Alert, {
25
+ tone: "danger",
26
+ children: [/* @__PURE__ */ jsx("p", { children: intl.formatMessage(appMessages.previewError) }), /* @__PURE__ */ jsx(Button, {
27
+ size: "sm",
28
+ onPress: () => void refetch(),
29
+ children: intl.formatMessage(appMessages.retry)
30
+ })]
31
+ }) : /* @__PURE__ */ jsx(Spinner, { showLabel: true });
32
+ $[0] = intl;
33
+ $[1] = isError;
34
+ $[2] = refetch;
35
+ $[3] = t1;
36
+ } else t1 = $[3];
37
+ return t1;
38
+ }
39
+ let t1;
40
+ if ($[4] !== data.url || $[5] !== intl) {
41
+ t1 = canOpen(data.url) ? /* @__PURE__ */ jsxs(Link, {
42
+ href: data.url,
43
+ target: "_blank",
44
+ rel: "noopener noreferrer",
45
+ className: ItemPanel_module_default.previewLink,
46
+ children: [/* @__PURE__ */ jsx(Icon, {
47
+ icon: ExternalLink,
48
+ size: "sm"
49
+ }), intl.formatMessage(appMessages.openPreview)]
50
+ }) : null;
51
+ $[4] = data.url;
52
+ $[5] = intl;
53
+ $[6] = t1;
54
+ } else t1 = $[6];
55
+ const t2 = data.url;
56
+ let t3;
57
+ if ($[7] !== intl || $[8] !== title) {
58
+ t3 = intl.formatMessage(appMessages.previewOf, { title });
59
+ $[7] = intl;
60
+ $[8] = title;
61
+ $[9] = t3;
62
+ } else t3 = $[9];
63
+ let t4;
64
+ if ($[10] !== data.url || $[11] !== t3) {
65
+ t4 = /* @__PURE__ */ jsx("iframe", {
66
+ src: t2,
67
+ title: t3,
68
+ className: ItemPanel_module_default.frame
69
+ });
70
+ $[10] = data.url;
71
+ $[11] = t3;
72
+ $[12] = t4;
73
+ } else t4 = $[12];
74
+ let t5;
75
+ if ($[13] !== t1 || $[14] !== t4) {
76
+ t5 = /* @__PURE__ */ jsxs("div", {
77
+ className: ItemPanel_module_default.preview,
78
+ children: [t1, t4]
79
+ });
80
+ $[13] = t1;
81
+ $[14] = t4;
82
+ $[15] = t5;
83
+ } else t5 = $[15];
84
+ return t5;
85
+ }
86
+ /**
87
+ * The preview of an item, as its resource's `features.preview` says (decision 0009): a component
88
+ * of a plugin (`previews`), or the item's page on the site in an iframe, from a short-lived URL
89
+ * of the backend.
90
+ */
91
+ function ItemPreview(t0) {
92
+ const $ = c(21);
93
+ const { resource, item, title, locale } = t0;
94
+ const intl = usePanneauIntl();
95
+ const plugins = usePlugins();
96
+ const preview = resource.features?.preview ?? null;
97
+ if (preview === null) return null;
98
+ if (preview.mode === "iframe") {
99
+ let t1;
100
+ if ($[0] !== item || $[1] !== resource || $[2] !== title) {
101
+ t1 = /* @__PURE__ */ jsx(IframePreview, {
102
+ resource,
103
+ item,
104
+ title
105
+ });
106
+ $[0] = item;
107
+ $[1] = resource;
108
+ $[2] = title;
109
+ $[3] = t1;
110
+ } else t1 = $[3];
111
+ return t1;
112
+ }
113
+ const name = preview.component ?? "";
114
+ const t1 = plugins.previews;
115
+ let t2;
116
+ let t3;
117
+ let t4;
118
+ if ($[4] !== intl || $[5] !== item || $[6] !== locale || $[7] !== name || $[8] !== plugins.previews || $[9] !== resource) {
119
+ t4 = Symbol.for("react.early_return_sentinel");
120
+ bb0: {
121
+ const Component = t1.get(name);
122
+ if (Component === void 0) {
123
+ let t5;
124
+ if ($[13] !== intl || $[14] !== name) {
125
+ t5 = intl.formatMessage(appMessages.previewMissing, { component: name });
126
+ $[13] = intl;
127
+ $[14] = name;
128
+ $[15] = t5;
129
+ } else t5 = $[15];
130
+ let t6;
131
+ if ($[16] !== t5) {
132
+ t6 = /* @__PURE__ */ jsx(Alert, {
133
+ tone: "warning",
134
+ children: t5
135
+ });
136
+ $[16] = t5;
137
+ $[17] = t6;
138
+ } else t6 = $[17];
139
+ t4 = t6;
140
+ break bb0;
141
+ }
142
+ t2 = ItemPanel_module_default.preview;
143
+ t3 = createElement(Component, {
144
+ item,
145
+ resource,
146
+ locale
147
+ });
148
+ }
149
+ $[4] = intl;
150
+ $[5] = item;
151
+ $[6] = locale;
152
+ $[7] = name;
153
+ $[8] = plugins.previews;
154
+ $[9] = resource;
155
+ $[10] = t2;
156
+ $[11] = t3;
157
+ $[12] = t4;
158
+ } else {
159
+ t2 = $[10];
160
+ t3 = $[11];
161
+ t4 = $[12];
162
+ }
163
+ if (t4 !== Symbol.for("react.early_return_sentinel")) return t4;
164
+ let t5;
165
+ if ($[18] !== t2 || $[19] !== t3) {
166
+ t5 = /* @__PURE__ */ jsx("div", {
167
+ className: t2,
168
+ children: t3
169
+ });
170
+ $[18] = t2;
171
+ $[19] = t3;
172
+ $[20] = t5;
173
+ } else t5 = $[20];
174
+ return t5;
175
+ }
176
+ //#endregion
177
+ export { ItemPreview };
178
+
179
+ //# sourceMappingURL=ItemPreview.js.map