@backstage/plugin-home 0.5.9 → 0.5.10-next.1
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/CHANGELOG.md +37 -0
- package/alpha/package.json +7 -0
- package/dist/alpha.d.ts +12 -0
- package/dist/alpha.esm.js +39 -0
- package/dist/alpha.esm.js.map +1 -0
- package/dist/esm/{Content-a00a3132.esm.js → Content-06ba3c7b.esm.js} +2 -2
- package/dist/esm/{Content-a00a3132.esm.js.map → Content-06ba3c7b.esm.js.map} +1 -1
- package/dist/esm/{RecentlyVisited-618bf2be.esm.js → RecentlyVisited-3a78670a.esm.js} +9 -9
- package/dist/esm/{RecentlyVisited-618bf2be.esm.js.map → RecentlyVisited-3a78670a.esm.js.map} +1 -1
- package/dist/esm/{TopVisited-918c6b5f.esm.js → TopVisited-f4e8def1.esm.js} +9 -9
- package/dist/esm/{TopVisited-918c6b5f.esm.js.map → TopVisited-f4e8def1.esm.js.map} +1 -1
- package/dist/esm/VisitListener-fa6f752b.esm.js +613 -0
- package/dist/esm/VisitListener-fa6f752b.esm.js.map +1 -0
- package/dist/esm/{index-20932a73.esm.js → index-20e7c88d.esm.js} +6 -6
- package/dist/esm/{index-20932a73.esm.js.map → index-20e7c88d.esm.js.map} +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +54 -619
- package/dist/index.esm.js.map +1 -1
- package/package.json +35 -23
package/dist/index.esm.js
CHANGED
|
@@ -1,31 +1,34 @@
|
|
|
1
|
-
import { createRouteRef,
|
|
1
|
+
import { createRouteRef, createPlugin, createApiFactory, storageApiRef, identityApiRef, createRoutableExtension, createComponentExtension } from '@backstage/core-plugin-api';
|
|
2
2
|
import { createCardExtension as createCardExtension$1, SettingsModal as SettingsModal$1 } from '@backstage/plugin-home-react';
|
|
3
3
|
import { WebStorage } from '@backstage/core-app-api';
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import
|
|
4
|
+
import { v as visitsApiRef } from './esm/VisitListener-fa6f752b.esm.js';
|
|
5
|
+
export { C as CustomHomepageGrid, V as VisitListener, v as visitsApiRef } from './esm/VisitListener-fa6f752b.esm.js';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import 'react-router-dom';
|
|
8
|
+
import { makeStyles } from '@material-ui/core';
|
|
9
|
+
import 'react-grid-layout';
|
|
7
10
|
import 'react-grid-layout/css/styles.css';
|
|
8
11
|
import 'react-resizable/css/styles.css';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
import
|
|
12
|
+
import 'lodash';
|
|
13
|
+
import 'react-use/lib/useObservable';
|
|
14
|
+
import '@backstage/core-components';
|
|
15
|
+
import '@material-ui/core/Typography';
|
|
16
|
+
import '@material-ui/core/IconButton';
|
|
17
|
+
import '@material-ui/icons/Settings';
|
|
18
|
+
import '@material-ui/icons/Delete';
|
|
19
|
+
import '@rjsf/core';
|
|
20
|
+
import '@rjsf/material-ui';
|
|
21
|
+
import '@rjsf/validator-ajv8';
|
|
22
|
+
import '@material-ui/core/List';
|
|
23
|
+
import '@material-ui/core/ListItem';
|
|
24
|
+
import '@material-ui/icons/Add';
|
|
25
|
+
import '@material-ui/core/ListItemText';
|
|
26
|
+
import '@material-ui/core/Button';
|
|
27
|
+
import '@material-ui/icons/Save';
|
|
28
|
+
import '@material-ui/icons/Edit';
|
|
29
|
+
import '@material-ui/icons/Cancel';
|
|
30
|
+
import 'zod';
|
|
31
|
+
import '@backstage/catalog-model';
|
|
29
32
|
|
|
30
33
|
const rootRouteRef = createRouteRef({
|
|
31
34
|
id: "home"
|
|
@@ -110,21 +113,35 @@ class VisitsStorageApi {
|
|
|
110
113
|
return visit;
|
|
111
114
|
}
|
|
112
115
|
async persistAll(visits) {
|
|
113
|
-
const
|
|
114
|
-
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
|
116
|
+
const storageKey = await this.getStorageKey();
|
|
115
117
|
return this.storageApi.set(storageKey, visits);
|
|
116
118
|
}
|
|
117
119
|
async retrieveAll() {
|
|
118
120
|
var _a;
|
|
121
|
+
const storageKey = await this.getStorageKey();
|
|
122
|
+
const snapshot = this.storageApi.snapshot(storageKey);
|
|
123
|
+
if ((snapshot == null ? void 0 : snapshot.presence) !== "unknown") {
|
|
124
|
+
return (_a = snapshot == null ? void 0 : snapshot.value) != null ? _a : [];
|
|
125
|
+
}
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const subsription = this.storageApi.observe$(storageKey).subscribe({
|
|
128
|
+
next: (next) => {
|
|
129
|
+
var _a2;
|
|
130
|
+
const visits = (_a2 = next.value) != null ? _a2 : [];
|
|
131
|
+
subsription.unsubscribe();
|
|
132
|
+
resolve(visits);
|
|
133
|
+
},
|
|
134
|
+
error: (err) => {
|
|
135
|
+
subsription.unsubscribe();
|
|
136
|
+
reject(err);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async getStorageKey() {
|
|
119
142
|
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
|
120
143
|
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
visits = (_a = this.storageApi.snapshot(storageKey).value) != null ? _a : [];
|
|
124
|
-
} catch {
|
|
125
|
-
visits = [];
|
|
126
|
-
}
|
|
127
|
-
return visits;
|
|
144
|
+
return storageKey;
|
|
128
145
|
}
|
|
129
146
|
// This assumes Visit fields are either numbers or strings
|
|
130
147
|
compare(order, a, b) {
|
|
@@ -143,10 +160,6 @@ class VisitsWebStorageApi {
|
|
|
143
160
|
}
|
|
144
161
|
}
|
|
145
162
|
|
|
146
|
-
const visitsApiRef = createApiRef({
|
|
147
|
-
id: "homepage.visits"
|
|
148
|
-
});
|
|
149
|
-
|
|
150
163
|
const homePlugin = createPlugin({
|
|
151
164
|
id: "home",
|
|
152
165
|
apis: [
|
|
@@ -166,7 +179,7 @@ const homePlugin = createPlugin({
|
|
|
166
179
|
const HomepageCompositionRoot = homePlugin.provide(
|
|
167
180
|
createRoutableExtension({
|
|
168
181
|
name: "HomepageCompositionRoot",
|
|
169
|
-
component: () => import('./esm/index-
|
|
182
|
+
component: () => import('./esm/index-20e7c88d.esm.js').then((m) => m.HomepageCompositionRoot),
|
|
170
183
|
mountPoint: rootRouteRef
|
|
171
184
|
})
|
|
172
185
|
);
|
|
@@ -263,593 +276,15 @@ const HeaderWorldClock = homePlugin.provide(
|
|
|
263
276
|
const HomePageTopVisited = homePlugin.provide(
|
|
264
277
|
createCardExtension$1({
|
|
265
278
|
name: "HomePageTopVisited",
|
|
266
|
-
components: () => import('./esm/TopVisited-
|
|
279
|
+
components: () => import('./esm/TopVisited-f4e8def1.esm.js')
|
|
267
280
|
})
|
|
268
281
|
);
|
|
269
282
|
const HomePageRecentlyVisited = homePlugin.provide(
|
|
270
283
|
createCardExtension$1({
|
|
271
284
|
name: "HomePageRecentlyVisited",
|
|
272
|
-
components: () => import('./esm/RecentlyVisited-
|
|
273
|
-
})
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
const Form = withTheme(require("@rjsf/material-ui-v5").Theme);
|
|
277
|
-
const useStyles$3 = makeStyles(
|
|
278
|
-
(theme) => createStyles({
|
|
279
|
-
iconGrid: {
|
|
280
|
-
height: "100%",
|
|
281
|
-
"& *": {
|
|
282
|
-
padding: 0
|
|
283
|
-
}
|
|
284
|
-
},
|
|
285
|
-
settingsOverlay: {
|
|
286
|
-
position: "absolute",
|
|
287
|
-
backgroundColor: "rgba(40, 40, 40, 0.93)",
|
|
288
|
-
width: "100%",
|
|
289
|
-
height: "100%",
|
|
290
|
-
top: 0,
|
|
291
|
-
left: 0,
|
|
292
|
-
padding: theme.spacing(2),
|
|
293
|
-
color: "white"
|
|
294
|
-
}
|
|
295
|
-
})
|
|
296
|
-
);
|
|
297
|
-
const WidgetSettingsOverlay = (props) => {
|
|
298
|
-
const { id, widget, settings, handleRemove, handleSettingsSave, deletable } = props;
|
|
299
|
-
const [settingsDialogOpen, setSettingsDialogOpen] = React.useState(false);
|
|
300
|
-
const styles = useStyles$3();
|
|
301
|
-
return /* @__PURE__ */ React.createElement("div", { className: styles.settingsOverlay }, widget.settingsSchema && /* @__PURE__ */ React.createElement(
|
|
302
|
-
Dialog,
|
|
303
|
-
{
|
|
304
|
-
open: settingsDialogOpen,
|
|
305
|
-
className: "widgetSettingsDialog",
|
|
306
|
-
onClose: () => setSettingsDialogOpen(false)
|
|
307
|
-
},
|
|
308
|
-
/* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(
|
|
309
|
-
Form,
|
|
310
|
-
{
|
|
311
|
-
validator,
|
|
312
|
-
showErrorList: false,
|
|
313
|
-
schema: widget.settingsSchema,
|
|
314
|
-
uiSchema: widget.uiSchema,
|
|
315
|
-
noHtml5Validate: true,
|
|
316
|
-
formData: settings,
|
|
317
|
-
formContext: { settings },
|
|
318
|
-
onSubmit: ({ formData, errors }) => {
|
|
319
|
-
if (errors.length === 0) {
|
|
320
|
-
handleSettingsSave(id, formData);
|
|
321
|
-
setSettingsDialogOpen(false);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
))
|
|
326
|
-
), /* @__PURE__ */ React.createElement(
|
|
327
|
-
Grid,
|
|
328
|
-
{
|
|
329
|
-
container: true,
|
|
330
|
-
className: styles.iconGrid,
|
|
331
|
-
alignItems: "center",
|
|
332
|
-
justifyContent: "center"
|
|
333
|
-
},
|
|
334
|
-
widget.settingsSchema && /* @__PURE__ */ React.createElement(Grid, { item: true, className: "overlayGridItem" }, /* @__PURE__ */ React.createElement(Tooltip, { title: "Edit settings" }, /* @__PURE__ */ React.createElement(
|
|
335
|
-
IconButton,
|
|
336
|
-
{
|
|
337
|
-
color: "primary",
|
|
338
|
-
onClick: () => setSettingsDialogOpen(true)
|
|
339
|
-
},
|
|
340
|
-
/* @__PURE__ */ React.createElement(SettingsIcon, { fontSize: "large" })
|
|
341
|
-
))),
|
|
342
|
-
deletable !== false && /* @__PURE__ */ React.createElement(Grid, { item: true, className: "overlayGridItem" }, /* @__PURE__ */ React.createElement(Tooltip, { title: "Delete widget" }, /* @__PURE__ */ React.createElement(IconButton, { color: "secondary", onClick: () => handleRemove(id) }, /* @__PURE__ */ React.createElement(DeleteIcon, { fontSize: "large" }))))
|
|
343
|
-
));
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
const getTitle = (widget) => {
|
|
347
|
-
return widget.title || widget.name;
|
|
348
|
-
};
|
|
349
|
-
const AddWidgetDialog = (props) => {
|
|
350
|
-
const { widgets, handleAdd } = props;
|
|
351
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DialogTitle, null, "Add new widget to dashboard"), /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(List, { dense: true }, widgets.map((widget) => {
|
|
352
|
-
return /* @__PURE__ */ React.createElement(
|
|
353
|
-
ListItem,
|
|
354
|
-
{
|
|
355
|
-
key: widget.name,
|
|
356
|
-
button: true,
|
|
357
|
-
onClick: () => handleAdd(widget)
|
|
358
|
-
},
|
|
359
|
-
/* @__PURE__ */ React.createElement(ListItemAvatar, null, /* @__PURE__ */ React.createElement(AddIcon, null)),
|
|
360
|
-
/* @__PURE__ */ React.createElement(
|
|
361
|
-
ListItemText,
|
|
362
|
-
{
|
|
363
|
-
secondary: widget.description && /* @__PURE__ */ React.createElement(
|
|
364
|
-
Typography,
|
|
365
|
-
{
|
|
366
|
-
component: "span",
|
|
367
|
-
variant: "caption",
|
|
368
|
-
color: "textPrimary"
|
|
369
|
-
},
|
|
370
|
-
widget.description
|
|
371
|
-
),
|
|
372
|
-
primary: /* @__PURE__ */ React.createElement(Typography, { variant: "body1", color: "textPrimary" }, getTitle(widget))
|
|
373
|
-
}
|
|
374
|
-
)
|
|
375
|
-
);
|
|
376
|
-
}))));
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
const useStyles$2 = makeStyles(
|
|
380
|
-
(theme) => createStyles({
|
|
381
|
-
contentHeaderBtn: {
|
|
382
|
-
marginLeft: theme.spacing(2)
|
|
383
|
-
},
|
|
384
|
-
widgetWrapper: {
|
|
385
|
-
"& > *:first-child": {
|
|
386
|
-
width: "100%",
|
|
387
|
-
height: "100%"
|
|
388
|
-
}
|
|
389
|
-
}
|
|
285
|
+
components: () => import('./esm/RecentlyVisited-3a78670a.esm.js')
|
|
390
286
|
})
|
|
391
287
|
);
|
|
392
|
-
const CustomHomepageButtons = (props) => {
|
|
393
|
-
const {
|
|
394
|
-
editMode,
|
|
395
|
-
numWidgets,
|
|
396
|
-
clearLayout,
|
|
397
|
-
setAddWidgetDialogOpen,
|
|
398
|
-
changeEditMode,
|
|
399
|
-
defaultConfigAvailable,
|
|
400
|
-
restoreDefault
|
|
401
|
-
} = props;
|
|
402
|
-
const styles = useStyles$2();
|
|
403
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, !editMode && numWidgets > 0 ? /* @__PURE__ */ React.createElement(
|
|
404
|
-
Button,
|
|
405
|
-
{
|
|
406
|
-
variant: "contained",
|
|
407
|
-
color: "primary",
|
|
408
|
-
onClick: () => changeEditMode(true),
|
|
409
|
-
size: "small",
|
|
410
|
-
startIcon: /* @__PURE__ */ React.createElement(EditIcon, null)
|
|
411
|
-
},
|
|
412
|
-
"Edit"
|
|
413
|
-
) : /* @__PURE__ */ React.createElement(React.Fragment, null, defaultConfigAvailable && /* @__PURE__ */ React.createElement(
|
|
414
|
-
Button,
|
|
415
|
-
{
|
|
416
|
-
variant: "contained",
|
|
417
|
-
className: styles.contentHeaderBtn,
|
|
418
|
-
onClick: restoreDefault,
|
|
419
|
-
size: "small",
|
|
420
|
-
startIcon: /* @__PURE__ */ React.createElement(CancelIcon, null)
|
|
421
|
-
},
|
|
422
|
-
"Restore defaults"
|
|
423
|
-
), numWidgets > 0 && /* @__PURE__ */ React.createElement(
|
|
424
|
-
Button,
|
|
425
|
-
{
|
|
426
|
-
variant: "contained",
|
|
427
|
-
color: "secondary",
|
|
428
|
-
className: styles.contentHeaderBtn,
|
|
429
|
-
onClick: clearLayout,
|
|
430
|
-
size: "small",
|
|
431
|
-
startIcon: /* @__PURE__ */ React.createElement(DeleteIcon, null)
|
|
432
|
-
},
|
|
433
|
-
"Clear all"
|
|
434
|
-
), /* @__PURE__ */ React.createElement(
|
|
435
|
-
Button,
|
|
436
|
-
{
|
|
437
|
-
variant: "contained",
|
|
438
|
-
className: styles.contentHeaderBtn,
|
|
439
|
-
onClick: () => setAddWidgetDialogOpen(true),
|
|
440
|
-
size: "small",
|
|
441
|
-
startIcon: /* @__PURE__ */ React.createElement(AddIcon, null)
|
|
442
|
-
},
|
|
443
|
-
"Add widget"
|
|
444
|
-
), numWidgets > 0 && /* @__PURE__ */ React.createElement(
|
|
445
|
-
Button,
|
|
446
|
-
{
|
|
447
|
-
className: styles.contentHeaderBtn,
|
|
448
|
-
variant: "contained",
|
|
449
|
-
color: "primary",
|
|
450
|
-
onClick: () => changeEditMode(false),
|
|
451
|
-
size: "small",
|
|
452
|
-
startIcon: /* @__PURE__ */ React.createElement(SaveIcon, null)
|
|
453
|
-
},
|
|
454
|
-
"Save"
|
|
455
|
-
)));
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const RSJFTypeSchema = z.any();
|
|
459
|
-
const RSJFTypeUiSchema = z.any();
|
|
460
|
-
const ReactElementSchema = z.any();
|
|
461
|
-
const LayoutSchema = z.any();
|
|
462
|
-
const LayoutConfigurationSchema = z.object({
|
|
463
|
-
component: ReactElementSchema,
|
|
464
|
-
x: z.number().nonnegative("x must be positive number"),
|
|
465
|
-
y: z.number().nonnegative("y must be positive number"),
|
|
466
|
-
width: z.number().positive("width must be positive number"),
|
|
467
|
-
height: z.number().positive("height must be positive number"),
|
|
468
|
-
movable: z.boolean().optional(),
|
|
469
|
-
deletable: z.boolean().optional(),
|
|
470
|
-
resizable: z.boolean().optional()
|
|
471
|
-
});
|
|
472
|
-
const WidgetSchema = z.object({
|
|
473
|
-
name: z.string(),
|
|
474
|
-
title: z.string().optional(),
|
|
475
|
-
description: z.string().optional(),
|
|
476
|
-
component: ReactElementSchema,
|
|
477
|
-
width: z.number().positive("width must be positive number").optional(),
|
|
478
|
-
height: z.number().positive("height must be positive number").optional(),
|
|
479
|
-
minWidth: z.number().positive("minWidth must be positive number").optional(),
|
|
480
|
-
maxWidth: z.number().positive("maxWidth must be positive number").optional(),
|
|
481
|
-
minHeight: z.number().positive("minHeight must be positive number").optional(),
|
|
482
|
-
maxHeight: z.number().positive("maxHeight must be positive number").optional(),
|
|
483
|
-
settingsSchema: RSJFTypeSchema.optional(),
|
|
484
|
-
uiSchema: RSJFTypeUiSchema.optional(),
|
|
485
|
-
movable: z.boolean().optional(),
|
|
486
|
-
deletable: z.boolean().optional(),
|
|
487
|
-
resizable: z.boolean().optional()
|
|
488
|
-
});
|
|
489
|
-
const GridWidgetSchema = z.object({
|
|
490
|
-
id: z.string(),
|
|
491
|
-
layout: LayoutSchema,
|
|
492
|
-
settings: z.record(z.string(), z.any()),
|
|
493
|
-
movable: z.boolean().optional(),
|
|
494
|
-
deletable: z.boolean().optional(),
|
|
495
|
-
resizable: z.boolean().optional()
|
|
496
|
-
});
|
|
497
|
-
const CustomHomepageGridStateV1Schema = z.object({
|
|
498
|
-
version: z.literal(1),
|
|
499
|
-
pages: z.record(z.string(), z.array(GridWidgetSchema))
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
const ResponsiveGrid = WidthProvider(Responsive);
|
|
503
|
-
const useStyles$1 = makeStyles(
|
|
504
|
-
(theme) => createStyles({
|
|
505
|
-
responsiveGrid: {
|
|
506
|
-
"& .react-grid-item > .react-resizable-handle:after": {
|
|
507
|
-
position: "absolute",
|
|
508
|
-
content: '""',
|
|
509
|
-
borderStyle: "solid",
|
|
510
|
-
borderWidth: "0 0 20px 20px",
|
|
511
|
-
borderColor: `transparent transparent ${theme.palette.primary.light} transparent`
|
|
512
|
-
}
|
|
513
|
-
},
|
|
514
|
-
contentHeaderBtn: {
|
|
515
|
-
marginLeft: theme.spacing(2)
|
|
516
|
-
},
|
|
517
|
-
widgetWrapper: {
|
|
518
|
-
'& > div[class*="MuiCard-root"]': {
|
|
519
|
-
width: "100%",
|
|
520
|
-
height: "100%"
|
|
521
|
-
},
|
|
522
|
-
'& div[class*="MuiCardContent-root"]': {
|
|
523
|
-
overflow: "auto"
|
|
524
|
-
},
|
|
525
|
-
"& + .react-grid-placeholder": {
|
|
526
|
-
backgroundColor: theme.palette.primary.light
|
|
527
|
-
},
|
|
528
|
-
"&.edit > :active": {
|
|
529
|
-
cursor: "move"
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
})
|
|
533
|
-
);
|
|
534
|
-
function useHomeStorage(defaultWidgets) {
|
|
535
|
-
const key = "home";
|
|
536
|
-
const storageApi = useApi(storageApiRef).forBucket("home.customHomepage");
|
|
537
|
-
const setWidgets = useCallback(
|
|
538
|
-
(value) => {
|
|
539
|
-
const grid = {
|
|
540
|
-
version: 1,
|
|
541
|
-
pages: {
|
|
542
|
-
default: value
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
storageApi.set(key, JSON.stringify(grid));
|
|
546
|
-
},
|
|
547
|
-
[key, storageApi]
|
|
548
|
-
);
|
|
549
|
-
const homeSnapshot = useObservable(
|
|
550
|
-
storageApi.observe$(key),
|
|
551
|
-
storageApi.snapshot(key)
|
|
552
|
-
);
|
|
553
|
-
const widgets = useMemo(() => {
|
|
554
|
-
if (homeSnapshot.presence === "absent") {
|
|
555
|
-
return defaultWidgets;
|
|
556
|
-
}
|
|
557
|
-
try {
|
|
558
|
-
const grid = JSON.parse(homeSnapshot.value);
|
|
559
|
-
return CustomHomepageGridStateV1Schema.parse(grid).pages.default;
|
|
560
|
-
} catch (e) {
|
|
561
|
-
return defaultWidgets;
|
|
562
|
-
}
|
|
563
|
-
}, [homeSnapshot, defaultWidgets]);
|
|
564
|
-
return [widgets, setWidgets];
|
|
565
|
-
}
|
|
566
|
-
const convertConfigToDefaultWidgets = (config, availableWidgets) => {
|
|
567
|
-
const ret = config.map((conf, i) => {
|
|
568
|
-
var _a, _b;
|
|
569
|
-
const c = LayoutConfigurationSchema.parse(conf);
|
|
570
|
-
const name = React.isValidElement(c.component) ? getComponentData(c.component, "core.extensionName") : c.component;
|
|
571
|
-
if (!name) {
|
|
572
|
-
return null;
|
|
573
|
-
}
|
|
574
|
-
const widget = availableWidgets.find((w) => w.name === name);
|
|
575
|
-
if (!widget) {
|
|
576
|
-
return null;
|
|
577
|
-
}
|
|
578
|
-
const widgetId = `${widget.name}__${i}${Math.random().toString(36).slice(2)}`;
|
|
579
|
-
return {
|
|
580
|
-
id: widgetId,
|
|
581
|
-
layout: {
|
|
582
|
-
i: widgetId,
|
|
583
|
-
x: c.x,
|
|
584
|
-
y: c.y,
|
|
585
|
-
w: Math.min((_a = widget.maxWidth) != null ? _a : Number.MAX_VALUE, c.width),
|
|
586
|
-
h: Math.min((_b = widget.maxHeight) != null ? _b : Number.MAX_VALUE, c.height),
|
|
587
|
-
minW: widget.minWidth,
|
|
588
|
-
maxW: widget.maxWidth,
|
|
589
|
-
minH: widget.minHeight,
|
|
590
|
-
maxH: widget.maxHeight,
|
|
591
|
-
isDraggable: false,
|
|
592
|
-
isResizable: false
|
|
593
|
-
},
|
|
594
|
-
settings: {},
|
|
595
|
-
movable: conf.movable,
|
|
596
|
-
deletable: conf.deletable,
|
|
597
|
-
resizable: conf.resizable
|
|
598
|
-
};
|
|
599
|
-
});
|
|
600
|
-
return compact(ret);
|
|
601
|
-
};
|
|
602
|
-
const availableWidgetsFilter = (elements) => {
|
|
603
|
-
return elements.selectByComponentData({
|
|
604
|
-
key: "core.extensionName"
|
|
605
|
-
}).getElements().flatMap((elem) => {
|
|
606
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
607
|
-
const config = getComponentData(elem, "home.widget.config");
|
|
608
|
-
return [
|
|
609
|
-
WidgetSchema.parse({
|
|
610
|
-
component: elem,
|
|
611
|
-
name: getComponentData(elem, "core.extensionName"),
|
|
612
|
-
title: getComponentData(elem, "title"),
|
|
613
|
-
description: getComponentData(elem, "description"),
|
|
614
|
-
settingsSchema: (_a = config == null ? void 0 : config.settings) == null ? void 0 : _a.schema,
|
|
615
|
-
uiSchema: (_b = config == null ? void 0 : config.settings) == null ? void 0 : _b.uiSchema,
|
|
616
|
-
width: (_d = (_c = config == null ? void 0 : config.layout) == null ? void 0 : _c.width) == null ? void 0 : _d.defaultColumns,
|
|
617
|
-
minWidth: (_f = (_e = config == null ? void 0 : config.layout) == null ? void 0 : _e.width) == null ? void 0 : _f.minColumns,
|
|
618
|
-
maxWidth: (_h = (_g = config == null ? void 0 : config.layout) == null ? void 0 : _g.width) == null ? void 0 : _h.maxColumns,
|
|
619
|
-
height: (_j = (_i = config == null ? void 0 : config.layout) == null ? void 0 : _i.height) == null ? void 0 : _j.defaultRows,
|
|
620
|
-
minHeight: (_l = (_k = config == null ? void 0 : config.layout) == null ? void 0 : _k.height) == null ? void 0 : _l.minRows,
|
|
621
|
-
maxHeight: (_n = (_m = config == null ? void 0 : config.layout) == null ? void 0 : _m.height) == null ? void 0 : _n.maxRows
|
|
622
|
-
})
|
|
623
|
-
];
|
|
624
|
-
});
|
|
625
|
-
};
|
|
626
|
-
const CustomHomepageGrid = (props) => {
|
|
627
|
-
var _a;
|
|
628
|
-
const styles = useStyles$1();
|
|
629
|
-
const theme = useTheme();
|
|
630
|
-
const availableWidgets = useElementFilter(
|
|
631
|
-
props.children,
|
|
632
|
-
availableWidgetsFilter,
|
|
633
|
-
[props]
|
|
634
|
-
);
|
|
635
|
-
const defaultLayout = props.config ? convertConfigToDefaultWidgets(props.config, availableWidgets) : [];
|
|
636
|
-
const [widgets, setWidgets] = useHomeStorage(defaultLayout);
|
|
637
|
-
const [addWidgetDialogOpen, setAddWidgetDialogOpen] = React.useState(false);
|
|
638
|
-
const editModeOn = widgets.find((w) => w.layout.isResizable) !== void 0;
|
|
639
|
-
const [editMode, setEditMode] = React.useState(editModeOn);
|
|
640
|
-
const getWidgetByName = (name) => {
|
|
641
|
-
return availableWidgets.find((widget) => widget.name === name);
|
|
642
|
-
};
|
|
643
|
-
const getWidgetNameFromKey = (key) => {
|
|
644
|
-
return key.split("__")[0];
|
|
645
|
-
};
|
|
646
|
-
const handleAdd = (widget) => {
|
|
647
|
-
var _a2, _b, _c, _d;
|
|
648
|
-
const widgetId = `${widget.name}__${widgets.length + 1}${Math.random().toString(36).slice(2)}`;
|
|
649
|
-
setWidgets([
|
|
650
|
-
...widgets,
|
|
651
|
-
{
|
|
652
|
-
id: widgetId,
|
|
653
|
-
layout: {
|
|
654
|
-
i: widgetId,
|
|
655
|
-
x: 0,
|
|
656
|
-
y: Math.max(...widgets.map((w) => w.layout.y + w.layout.h)) + 1,
|
|
657
|
-
w: Math.min((_a2 = widget.maxWidth) != null ? _a2 : Number.MAX_VALUE, (_b = widget.width) != null ? _b : 12),
|
|
658
|
-
h: Math.min((_c = widget.maxHeight) != null ? _c : Number.MAX_VALUE, (_d = widget.height) != null ? _d : 4),
|
|
659
|
-
minW: widget.minWidth,
|
|
660
|
-
maxW: widget.maxWidth,
|
|
661
|
-
minH: widget.minHeight,
|
|
662
|
-
maxH: widget.maxHeight,
|
|
663
|
-
isResizable: editMode,
|
|
664
|
-
isDraggable: editMode
|
|
665
|
-
},
|
|
666
|
-
settings: {},
|
|
667
|
-
movable: widget.movable,
|
|
668
|
-
deletable: widget.deletable,
|
|
669
|
-
resizable: widget.resizable
|
|
670
|
-
}
|
|
671
|
-
]);
|
|
672
|
-
setAddWidgetDialogOpen(false);
|
|
673
|
-
};
|
|
674
|
-
const handleRemove = (widgetId) => {
|
|
675
|
-
setWidgets(widgets.filter((w) => w.id !== widgetId));
|
|
676
|
-
};
|
|
677
|
-
const handleSettingsSave = (widgetId, widgetSettings) => {
|
|
678
|
-
const idx = widgets.findIndex((w) => w.id === widgetId);
|
|
679
|
-
if (idx >= 0) {
|
|
680
|
-
const widget = widgets[idx];
|
|
681
|
-
widget.settings = widgetSettings;
|
|
682
|
-
widgets[idx] = widget;
|
|
683
|
-
setWidgets(widgets);
|
|
684
|
-
}
|
|
685
|
-
};
|
|
686
|
-
const clearLayout = () => {
|
|
687
|
-
setWidgets([]);
|
|
688
|
-
};
|
|
689
|
-
const changeEditMode = (mode) => {
|
|
690
|
-
setEditMode(mode);
|
|
691
|
-
setWidgets(
|
|
692
|
-
widgets.map((w) => {
|
|
693
|
-
const resizable = w.resizable === false ? false : mode;
|
|
694
|
-
const movable = w.movable === false ? false : mode;
|
|
695
|
-
return {
|
|
696
|
-
...w,
|
|
697
|
-
layout: { ...w.layout, isDraggable: movable, isResizable: resizable }
|
|
698
|
-
};
|
|
699
|
-
})
|
|
700
|
-
);
|
|
701
|
-
};
|
|
702
|
-
const handleLayoutChange = (newLayout, _) => {
|
|
703
|
-
if (editMode) {
|
|
704
|
-
const newWidgets = newLayout.map((l) => {
|
|
705
|
-
const widget = widgets.find((w) => w.id === l.i);
|
|
706
|
-
return {
|
|
707
|
-
...widget,
|
|
708
|
-
layout: l
|
|
709
|
-
};
|
|
710
|
-
});
|
|
711
|
-
setWidgets(newWidgets);
|
|
712
|
-
}
|
|
713
|
-
};
|
|
714
|
-
const handleRestoreDefaultConfig = () => {
|
|
715
|
-
setWidgets(
|
|
716
|
-
defaultLayout.map((w) => {
|
|
717
|
-
const resizable = w.resizable === false ? false : editMode;
|
|
718
|
-
const movable = w.movable === false ? false : editMode;
|
|
719
|
-
return {
|
|
720
|
-
...w,
|
|
721
|
-
layout: {
|
|
722
|
-
...w.layout,
|
|
723
|
-
isDraggable: movable,
|
|
724
|
-
isResizable: resizable
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
})
|
|
728
|
-
);
|
|
729
|
-
};
|
|
730
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "" }, /* @__PURE__ */ React.createElement(
|
|
731
|
-
CustomHomepageButtons,
|
|
732
|
-
{
|
|
733
|
-
editMode,
|
|
734
|
-
numWidgets: widgets.length,
|
|
735
|
-
clearLayout,
|
|
736
|
-
setAddWidgetDialogOpen,
|
|
737
|
-
changeEditMode,
|
|
738
|
-
defaultConfigAvailable: props.config !== void 0,
|
|
739
|
-
restoreDefault: handleRestoreDefaultConfig
|
|
740
|
-
}
|
|
741
|
-
)), /* @__PURE__ */ React.createElement(
|
|
742
|
-
Dialog,
|
|
743
|
-
{
|
|
744
|
-
open: addWidgetDialogOpen,
|
|
745
|
-
onClose: () => setAddWidgetDialogOpen(false)
|
|
746
|
-
},
|
|
747
|
-
/* @__PURE__ */ React.createElement(AddWidgetDialog, { widgets: availableWidgets, handleAdd })
|
|
748
|
-
), !editMode && widgets.length === 0 && /* @__PURE__ */ React.createElement(Typography, { variant: "h5", align: "center" }, "No widgets added. Start by clicking the 'Add widget' button."), /* @__PURE__ */ React.createElement(
|
|
749
|
-
ResponsiveGrid,
|
|
750
|
-
{
|
|
751
|
-
className: styles.responsiveGrid,
|
|
752
|
-
measureBeforeMount: true,
|
|
753
|
-
compactType: props.compactType,
|
|
754
|
-
style: props.style,
|
|
755
|
-
allowOverlap: props.allowOverlap,
|
|
756
|
-
preventCollision: props.preventCollision,
|
|
757
|
-
draggableCancel: ".overlayGridItem,.widgetSettingsDialog,.disabled",
|
|
758
|
-
containerPadding: props.containerPadding,
|
|
759
|
-
margin: props.containerMargin,
|
|
760
|
-
breakpoints: props.breakpoints ? props.breakpoints : theme.breakpoints.values,
|
|
761
|
-
cols: props.cols ? props.cols : { xl: 12, lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
|
|
762
|
-
rowHeight: (_a = props.rowHeight) != null ? _a : 60,
|
|
763
|
-
onLayoutChange: handleLayoutChange,
|
|
764
|
-
layouts: { xl: widgets.map((w) => w.layout) }
|
|
765
|
-
},
|
|
766
|
-
widgets.map((w) => {
|
|
767
|
-
var _a2;
|
|
768
|
-
const l = w.layout;
|
|
769
|
-
const widgetName = getWidgetNameFromKey(l.i);
|
|
770
|
-
const widget = getWidgetByName(widgetName);
|
|
771
|
-
if (!widget || !widget.component) {
|
|
772
|
-
return null;
|
|
773
|
-
}
|
|
774
|
-
const widgetProps = {
|
|
775
|
-
...widget.component.props,
|
|
776
|
-
...(_a2 = w.settings) != null ? _a2 : {}
|
|
777
|
-
};
|
|
778
|
-
return /* @__PURE__ */ React.createElement(
|
|
779
|
-
"div",
|
|
780
|
-
{
|
|
781
|
-
key: l.i,
|
|
782
|
-
className: `${styles.widgetWrapper} ${editMode && "edit"} ${w.movable === false && "disabled"}`
|
|
783
|
-
},
|
|
784
|
-
/* @__PURE__ */ React.createElement(ErrorBoundary, null, /* @__PURE__ */ React.createElement(widget.component.type, { ...widgetProps })),
|
|
785
|
-
editMode && /* @__PURE__ */ React.createElement(
|
|
786
|
-
WidgetSettingsOverlay,
|
|
787
|
-
{
|
|
788
|
-
id: l.i,
|
|
789
|
-
widget,
|
|
790
|
-
handleRemove,
|
|
791
|
-
handleSettingsSave,
|
|
792
|
-
settings: w.settings,
|
|
793
|
-
deletable: w.deletable
|
|
794
|
-
}
|
|
795
|
-
)
|
|
796
|
-
);
|
|
797
|
-
})
|
|
798
|
-
));
|
|
799
|
-
};
|
|
800
|
-
|
|
801
|
-
const getToEntityRef = ({
|
|
802
|
-
rootPath = "catalog",
|
|
803
|
-
stringifyEntityRefImpl = stringifyEntityRef
|
|
804
|
-
} = {}) => ({ pathname }) => {
|
|
805
|
-
const regex = new RegExp(
|
|
806
|
-
`^/${rootPath}/(?<namespace>[^/]+)/(?<kind>[^/]+)/(?<name>[^/]+)`
|
|
807
|
-
);
|
|
808
|
-
const result = regex.exec(pathname);
|
|
809
|
-
if (!result || !(result == null ? void 0 : result.groups))
|
|
810
|
-
return void 0;
|
|
811
|
-
const entity = {
|
|
812
|
-
namespace: result.groups.namespace,
|
|
813
|
-
kind: result.groups.kind,
|
|
814
|
-
name: result.groups.name
|
|
815
|
-
};
|
|
816
|
-
return stringifyEntityRefImpl(entity);
|
|
817
|
-
};
|
|
818
|
-
const getVisitName = ({ rootPath = "catalog", document = global.document } = {}) => ({ pathname }) => {
|
|
819
|
-
const regex = new RegExp(
|
|
820
|
-
`^/${rootPath}/(?<namespace>[^/]+)/(?<kind>[^/]+)/(?<name>[^/]+)`
|
|
821
|
-
);
|
|
822
|
-
let result = regex.exec(pathname);
|
|
823
|
-
if (result && (result == null ? void 0 : result.groups))
|
|
824
|
-
return result.groups.name;
|
|
825
|
-
result = /^\/(?<name>[^\/]+)$/.exec(pathname);
|
|
826
|
-
if (result && (result == null ? void 0 : result.groups))
|
|
827
|
-
return result.groups.name;
|
|
828
|
-
return document.title;
|
|
829
|
-
};
|
|
830
|
-
const VisitListener = ({
|
|
831
|
-
children,
|
|
832
|
-
toEntityRef,
|
|
833
|
-
visitName
|
|
834
|
-
}) => {
|
|
835
|
-
const visitsApi = useApi(visitsApiRef);
|
|
836
|
-
const { pathname } = useLocation();
|
|
837
|
-
const toEntityRefImpl = toEntityRef != null ? toEntityRef : getToEntityRef();
|
|
838
|
-
const visitNameImpl = visitName != null ? visitName : getVisitName();
|
|
839
|
-
useEffect(() => {
|
|
840
|
-
const requestId = requestAnimationFrame(() => {
|
|
841
|
-
visitsApi.save({
|
|
842
|
-
visit: {
|
|
843
|
-
name: visitNameImpl({ pathname }),
|
|
844
|
-
pathname,
|
|
845
|
-
entityRef: toEntityRefImpl({ pathname })
|
|
846
|
-
}
|
|
847
|
-
});
|
|
848
|
-
});
|
|
849
|
-
return () => cancelAnimationFrame(requestId);
|
|
850
|
-
}, [visitsApi, pathname, toEntityRefImpl, visitNameImpl]);
|
|
851
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
|
|
852
|
-
};
|
|
853
288
|
|
|
854
289
|
const TemplateBackstageLogo = (props) => {
|
|
855
290
|
return /* @__PURE__ */ React.createElement(
|
|
@@ -900,5 +335,5 @@ const TemplateBackstageLogoIcon = () => {
|
|
|
900
335
|
const createCardExtension = createCardExtension$1;
|
|
901
336
|
const SettingsModal = SettingsModal$1;
|
|
902
337
|
|
|
903
|
-
export { ComponentAccordion, ComponentTab, ComponentTabs,
|
|
338
|
+
export { ComponentAccordion, ComponentTab, ComponentTabs, HeaderWorldClock, HomePageCompanyLogo, HomePageRandomJoke, HomePageRecentlyVisited, HomePageStarredEntities, HomePageToolkit, HomePageTopVisited, HomepageCompositionRoot, SettingsModal, TemplateBackstageLogo, TemplateBackstageLogoIcon, VisitsStorageApi, VisitsWebStorageApi, WelcomeTitle, createCardExtension, homePlugin };
|
|
904
339
|
//# sourceMappingURL=index.esm.js.map
|