@backstage/plugin-home 0.5.8 → 0.5.9-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 +29 -0
- package/README.md +83 -0
- package/dist/esm/Content-a00a3132.esm.js +351 -0
- package/dist/esm/Content-a00a3132.esm.js.map +1 -0
- package/dist/esm/RecentlyVisited-618bf2be.esm.js +40 -0
- package/dist/esm/RecentlyVisited-618bf2be.esm.js.map +1 -0
- package/dist/esm/TopVisited-918c6b5f.esm.js +40 -0
- package/dist/esm/TopVisited-918c6b5f.esm.js.map +1 -0
- package/dist/esm/{index-e7416f2d.esm.js → index-20932a73.esm.js} +4 -2
- package/dist/esm/{index-e7416f2d.esm.js.map → index-20932a73.esm.js.map} +1 -1
- package/dist/index.d.ts +182 -2
- package/dist/index.esm.js +198 -5
- package/dist/index.esm.js.map +1 -1
- package/package.json +12 -9
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useOutlet } from 'react-router-dom';
|
|
3
|
-
export { CustomHomepageGrid } from '../index.esm.js';
|
|
3
|
+
export { CustomHomepageGrid, VisitListener } from '../index.esm.js';
|
|
4
4
|
import '@backstage/core-plugin-api';
|
|
5
5
|
import '@backstage/plugin-home-react';
|
|
6
|
+
import '@backstage/core-app-api';
|
|
6
7
|
import 'react-grid-layout';
|
|
7
8
|
import 'react-grid-layout/css/styles.css';
|
|
8
9
|
import 'react-resizable/css/styles.css';
|
|
@@ -25,6 +26,7 @@ import '@material-ui/icons/Save';
|
|
|
25
26
|
import '@material-ui/icons/Edit';
|
|
26
27
|
import '@material-ui/icons/Cancel';
|
|
27
28
|
import 'zod';
|
|
29
|
+
import '@backstage/catalog-model';
|
|
28
30
|
|
|
29
31
|
const HomepageCompositionRoot = (props) => {
|
|
30
32
|
var _a;
|
|
@@ -34,4 +36,4 @@ const HomepageCompositionRoot = (props) => {
|
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
export { HomepageCompositionRoot };
|
|
37
|
-
//# sourceMappingURL=index-
|
|
39
|
+
//# sourceMappingURL=index-20932a73.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-20932a73.esm.js","sources":["../../src/components/HomepageCompositionRoot.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ReactNode } from 'react';\nimport { useOutlet } from 'react-router-dom';\n\nexport const HomepageCompositionRoot = (props: {\n title?: string;\n children?: ReactNode;\n}) => {\n const outlet = useOutlet();\n const children = props.children ?? outlet;\n return <>{children}</>;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBa,MAAA,uBAAA,GAA0B,CAAC,KAGlC,KAAA;AAtBN,EAAA,IAAA,EAAA,CAAA;AAuBE,EAAA,MAAM,SAAS,SAAU,EAAA,CAAA;AACzB,EAAM,MAAA,QAAA,GAAA,CAAW,EAAM,GAAA,KAAA,CAAA,QAAA,KAAN,IAAkB,GAAA,EAAA,GAAA,MAAA,CAAA;AACnC,EAAA,iEAAU,QAAS,CAAA,CAAA;AACrB;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { createCardExtension as createCardExtension$1, CardExtensionProps as Car
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import React__default, { ReactNode, ReactElement } from 'react';
|
|
6
6
|
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
7
|
+
import { StorageApi, IdentityApi, ErrorApi } from '@backstage/core-plugin-api';
|
|
7
8
|
|
|
8
9
|
/** @public */
|
|
9
10
|
type Tool = {
|
|
@@ -32,10 +33,118 @@ type WelcomeTitleLanguageProps = {
|
|
|
32
33
|
language?: string[];
|
|
33
34
|
};
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @public
|
|
38
|
+
* Model for a visit entity.
|
|
39
|
+
*/
|
|
40
|
+
type Visit = {
|
|
41
|
+
/**
|
|
42
|
+
* The auto-generated visit identification.
|
|
43
|
+
*/
|
|
44
|
+
id: string;
|
|
45
|
+
/**
|
|
46
|
+
* The visited entity, usually an entity id.
|
|
47
|
+
*/
|
|
48
|
+
name: string;
|
|
49
|
+
/**
|
|
50
|
+
* The visited url pathname, usually the entity route.
|
|
51
|
+
*/
|
|
52
|
+
pathname: string;
|
|
53
|
+
/**
|
|
54
|
+
* An individual view count.
|
|
55
|
+
*/
|
|
56
|
+
hits: number;
|
|
57
|
+
/**
|
|
58
|
+
* Last date and time of visit. Format: unix epoch in ms.
|
|
59
|
+
*/
|
|
60
|
+
timestamp: number;
|
|
61
|
+
/**
|
|
62
|
+
* Optional entity reference. See stringifyEntityRef from catalog-model.
|
|
63
|
+
*/
|
|
64
|
+
entityRef?: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* @public
|
|
68
|
+
* This data structure represents the parameters associated with search queries for visits.
|
|
69
|
+
*/
|
|
70
|
+
type VisitsApiQueryParams = {
|
|
71
|
+
/**
|
|
72
|
+
* Limits the number of results returned. The default is 8.
|
|
73
|
+
*/
|
|
74
|
+
limit?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Allows ordering visits on entity properties.
|
|
77
|
+
* @example
|
|
78
|
+
* Sort ascending by the timestamp field.
|
|
79
|
+
* ```
|
|
80
|
+
* { orderBy: [{ field: 'timestamp', direction: 'asc' }] }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
orderBy?: Array<{
|
|
84
|
+
field: keyof Visit;
|
|
85
|
+
direction: 'asc' | 'desc';
|
|
86
|
+
}>;
|
|
87
|
+
/**
|
|
88
|
+
* Allows filtering visits on entity properties.
|
|
89
|
+
* @example
|
|
90
|
+
* Most popular docs on the past 7 days
|
|
91
|
+
* ```
|
|
92
|
+
* {
|
|
93
|
+
* orderBy: [{ field: 'hits', direction: 'desc' }],
|
|
94
|
+
* filterBy: [
|
|
95
|
+
* { field: 'timestamp', operator: '>=', value: <date> },
|
|
96
|
+
* { field: 'entityRef', operator: 'contains', value: 'docs' }
|
|
97
|
+
* ]
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
filterBy?: Array<{
|
|
102
|
+
field: keyof Visit;
|
|
103
|
+
operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains';
|
|
104
|
+
value: string | number;
|
|
105
|
+
}>;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* @public
|
|
109
|
+
* This data structure represents the parameters associated with saving visits.
|
|
110
|
+
*/
|
|
111
|
+
type VisitsApiSaveParams = {
|
|
112
|
+
visit: Omit<Visit, 'id' | 'hits' | 'timestamp'>;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* @public
|
|
116
|
+
* Visits API public contract.
|
|
117
|
+
*/
|
|
118
|
+
interface VisitsApi {
|
|
119
|
+
/**
|
|
120
|
+
* Persist a new visit.
|
|
121
|
+
* @param pageVisit - a new visit data
|
|
122
|
+
*/
|
|
123
|
+
save(saveParams: VisitsApiSaveParams): Promise<Visit>;
|
|
124
|
+
/**
|
|
125
|
+
* Get user visits.
|
|
126
|
+
* @param queryParams - optional search query params.
|
|
127
|
+
*/
|
|
128
|
+
list(queryParams?: VisitsApiQueryParams): Promise<Visit[]>;
|
|
129
|
+
}
|
|
130
|
+
/** @public */
|
|
131
|
+
declare const visitsApiRef: _backstage_core_plugin_api.ApiRef<VisitsApi>;
|
|
132
|
+
|
|
133
|
+
/** @public */
|
|
134
|
+
type VisitedByTypeKind = 'recent' | 'top';
|
|
135
|
+
/** @public */
|
|
136
|
+
type VisitedByTypeProps = {
|
|
137
|
+
visits?: Array<Visit>;
|
|
138
|
+
numVisitsOpen?: number;
|
|
139
|
+
numVisitsTotal?: number;
|
|
140
|
+
loading?: boolean;
|
|
141
|
+
kind: VisitedByTypeKind;
|
|
142
|
+
};
|
|
143
|
+
|
|
35
144
|
/** @public */
|
|
36
145
|
declare const homePlugin: _backstage_core_plugin_api.BackstagePlugin<{
|
|
37
146
|
root: _backstage_core_plugin_api.RouteRef<undefined>;
|
|
38
|
-
}, {}
|
|
147
|
+
}, {}>;
|
|
39
148
|
/** @public */
|
|
40
149
|
declare const HomepageCompositionRoot: (props: {
|
|
41
150
|
title?: string | undefined;
|
|
@@ -104,6 +213,16 @@ declare const HeaderWorldClock: (props: {
|
|
|
104
213
|
clockConfigs: ClockConfig[];
|
|
105
214
|
customTimeFormat?: Intl.DateTimeFormatOptions | undefined;
|
|
106
215
|
}) => React.JSX.Element | null;
|
|
216
|
+
/**
|
|
217
|
+
* Display top visited pages for the homepage
|
|
218
|
+
* @public
|
|
219
|
+
*/
|
|
220
|
+
declare const HomePageTopVisited: (props: _backstage_plugin_home_react.CardExtensionProps<Partial<VisitedByTypeProps>>) => React.JSX.Element;
|
|
221
|
+
/**
|
|
222
|
+
* Display recently visited pages for the homepage
|
|
223
|
+
* @public
|
|
224
|
+
*/
|
|
225
|
+
declare const HomePageRecentlyVisited: (props: _backstage_plugin_home_react.CardExtensionProps<Partial<VisitedByTypeProps>>) => React.JSX.Element;
|
|
107
226
|
|
|
108
227
|
/**
|
|
109
228
|
* Breakpoint options for <CustomHomepageGridProps/>
|
|
@@ -202,6 +321,21 @@ type LayoutConfiguration = {
|
|
|
202
321
|
*/
|
|
203
322
|
declare const CustomHomepageGrid: (props: CustomHomepageGridProps) => React__default.JSX.Element;
|
|
204
323
|
|
|
324
|
+
/**
|
|
325
|
+
* @public
|
|
326
|
+
* Component responsible for listening to location changes and calling
|
|
327
|
+
* the visitsApi to save visits.
|
|
328
|
+
*/
|
|
329
|
+
declare const VisitListener: ({ children, toEntityRef, visitName, }: {
|
|
330
|
+
children?: React__default.ReactNode;
|
|
331
|
+
toEntityRef?: (({ pathname }: {
|
|
332
|
+
pathname: string;
|
|
333
|
+
}) => string | undefined) | undefined;
|
|
334
|
+
visitName?: (({ pathname }: {
|
|
335
|
+
pathname: string;
|
|
336
|
+
}) => string) | undefined;
|
|
337
|
+
}) => JSX.Element;
|
|
338
|
+
|
|
205
339
|
/** @public */
|
|
206
340
|
declare const TemplateBackstageLogo: (props: {
|
|
207
341
|
classes: {
|
|
@@ -264,4 +398,50 @@ declare const SettingsModal: (props: {
|
|
|
264
398
|
children: JSX.Element;
|
|
265
399
|
}) => React.JSX.Element;
|
|
266
400
|
|
|
267
|
-
|
|
401
|
+
/** @public */
|
|
402
|
+
type VisitsStorageApiOptions = {
|
|
403
|
+
limit?: number;
|
|
404
|
+
storageApi: StorageApi;
|
|
405
|
+
identityApi: IdentityApi;
|
|
406
|
+
};
|
|
407
|
+
/**
|
|
408
|
+
* @public
|
|
409
|
+
* This is an implementation of VisitsApi that relies on a StorageApi.
|
|
410
|
+
* Beware that filtering and ordering are done in memory therefore it is
|
|
411
|
+
* prudent to keep limit to a reasonable size.
|
|
412
|
+
*/
|
|
413
|
+
declare class VisitsStorageApi implements VisitsApi {
|
|
414
|
+
private readonly limit;
|
|
415
|
+
private readonly storageApi;
|
|
416
|
+
private readonly storageKeyPrefix;
|
|
417
|
+
private readonly identityApi;
|
|
418
|
+
static create(options: VisitsStorageApiOptions): VisitsStorageApi;
|
|
419
|
+
private constructor();
|
|
420
|
+
/**
|
|
421
|
+
* Returns a list of visits through the visitsApi
|
|
422
|
+
*/
|
|
423
|
+
list(queryParams?: VisitsApiQueryParams): Promise<Visit[]>;
|
|
424
|
+
/**
|
|
425
|
+
* Saves a visit through the visitsApi
|
|
426
|
+
*/
|
|
427
|
+
save(saveParams: VisitsApiSaveParams): Promise<Visit>;
|
|
428
|
+
private persistAll;
|
|
429
|
+
private retrieveAll;
|
|
430
|
+
private compare;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** @public */
|
|
434
|
+
type VisitsWebStorageApiOptions = {
|
|
435
|
+
limit?: number;
|
|
436
|
+
identityApi: IdentityApi;
|
|
437
|
+
errorApi: ErrorApi;
|
|
438
|
+
};
|
|
439
|
+
/**
|
|
440
|
+
* @public
|
|
441
|
+
* This is a reference implementation of VisitsApi using WebStorage.
|
|
442
|
+
*/
|
|
443
|
+
declare class VisitsWebStorageApi {
|
|
444
|
+
static create(options: VisitsWebStorageApiOptions): VisitsStorageApi;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export { Breakpoint, CardConfig, CardExtensionProps, CardLayout, CardSettings, ClockConfig, ComponentAccordion, ComponentParts, ComponentRenderer, ComponentTab, ComponentTabs, CustomHomepageGrid, CustomHomepageGridProps, HeaderWorldClock, HomePageCompanyLogo, HomePageRandomJoke, HomePageRecentlyVisited, HomePageStarredEntities, HomePageToolkit, HomePageTopVisited, HomepageCompositionRoot, LayoutConfiguration, RendererProps, SettingsModal, TemplateBackstageLogo, TemplateBackstageLogoIcon, Tool, ToolkitContentProps, Visit, VisitListener, VisitedByTypeKind, VisitedByTypeProps, VisitsApi, VisitsApiQueryParams, VisitsApiSaveParams, VisitsStorageApi, VisitsStorageApiOptions, VisitsWebStorageApi, VisitsWebStorageApiOptions, WelcomeTitle, WelcomeTitleLanguageProps, createCardExtension, homePlugin, visitsApiRef };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { createRouteRef, createPlugin, createRoutableExtension, createComponentExtension, useElementFilter, useApi,
|
|
1
|
+
import { createRouteRef, createApiRef, createPlugin, createApiFactory, storageApiRef, identityApiRef, createRoutableExtension, createComponentExtension, useElementFilter, useApi, getComponentData } from '@backstage/core-plugin-api';
|
|
2
2
|
import { createCardExtension as createCardExtension$1, SettingsModal as SettingsModal$1 } from '@backstage/plugin-home-react';
|
|
3
|
-
import
|
|
4
|
-
import 'react
|
|
3
|
+
import { WebStorage } from '@backstage/core-app-api';
|
|
4
|
+
import React, { useCallback, useMemo, useEffect } from 'react';
|
|
5
|
+
import { useLocation } from 'react-router-dom';
|
|
5
6
|
import { WidthProvider, Responsive } from 'react-grid-layout';
|
|
6
7
|
import 'react-grid-layout/css/styles.css';
|
|
7
8
|
import 'react-resizable/css/styles.css';
|
|
@@ -24,13 +25,140 @@ import SaveIcon from '@material-ui/icons/Save';
|
|
|
24
25
|
import EditIcon from '@material-ui/icons/Edit';
|
|
25
26
|
import CancelIcon from '@material-ui/icons/Cancel';
|
|
26
27
|
import { z } from 'zod';
|
|
28
|
+
import { stringifyEntityRef } from '@backstage/catalog-model';
|
|
27
29
|
|
|
28
30
|
const rootRouteRef = createRouteRef({
|
|
29
31
|
id: "home"
|
|
30
32
|
});
|
|
31
33
|
|
|
34
|
+
var __defProp = Object.defineProperty;
|
|
35
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
36
|
+
var __publicField = (obj, key, value) => {
|
|
37
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
38
|
+
return value;
|
|
39
|
+
};
|
|
40
|
+
class VisitsStorageApi {
|
|
41
|
+
constructor(options) {
|
|
42
|
+
__publicField(this, "limit");
|
|
43
|
+
__publicField(this, "storageApi");
|
|
44
|
+
__publicField(this, "storageKeyPrefix", "@backstage/plugin-home:visits");
|
|
45
|
+
__publicField(this, "identityApi");
|
|
46
|
+
var _a;
|
|
47
|
+
this.limit = Math.abs((_a = options.limit) != null ? _a : 100);
|
|
48
|
+
this.storageApi = options.storageApi;
|
|
49
|
+
this.identityApi = options.identityApi;
|
|
50
|
+
}
|
|
51
|
+
static create(options) {
|
|
52
|
+
return new VisitsStorageApi(options);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Returns a list of visits through the visitsApi
|
|
56
|
+
*/
|
|
57
|
+
async list(queryParams) {
|
|
58
|
+
var _a, _b;
|
|
59
|
+
let visits = [...await this.retrieveAll()];
|
|
60
|
+
((_a = queryParams == null ? void 0 : queryParams.orderBy) != null ? _a : []).reverse().forEach((order) => {
|
|
61
|
+
if (order.direction === "asc") {
|
|
62
|
+
visits.sort((a, b) => this.compare(order, a, b));
|
|
63
|
+
} else {
|
|
64
|
+
visits.sort((a, b) => this.compare(order, b, a));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
((_b = queryParams == null ? void 0 : queryParams.filterBy) != null ? _b : []).reverse().forEach((filter) => {
|
|
68
|
+
visits = visits.filter((visit) => {
|
|
69
|
+
const field = visit[filter.field];
|
|
70
|
+
if (filter.operator === ">")
|
|
71
|
+
return field > filter.value;
|
|
72
|
+
if (filter.operator === ">=")
|
|
73
|
+
return field >= filter.value;
|
|
74
|
+
if (filter.operator === "<")
|
|
75
|
+
return field < filter.value;
|
|
76
|
+
if (filter.operator === "<=")
|
|
77
|
+
return field <= filter.value;
|
|
78
|
+
if (filter.operator === "==")
|
|
79
|
+
return field === filter.value;
|
|
80
|
+
if (filter.operator === "!=")
|
|
81
|
+
return field !== filter.value;
|
|
82
|
+
if (filter.operator === "contains")
|
|
83
|
+
return `${field}`.includes(`${filter.value}`);
|
|
84
|
+
return false;
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
return visits;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Saves a visit through the visitsApi
|
|
91
|
+
*/
|
|
92
|
+
async save(saveParams) {
|
|
93
|
+
const visits = [...await this.retrieveAll()];
|
|
94
|
+
const visit = {
|
|
95
|
+
...saveParams.visit,
|
|
96
|
+
id: window.crypto.randomUUID(),
|
|
97
|
+
hits: 1,
|
|
98
|
+
timestamp: Date.now()
|
|
99
|
+
};
|
|
100
|
+
const visitIndex = visits.findIndex((e) => e.pathname === visit.pathname);
|
|
101
|
+
if (visitIndex >= 0) {
|
|
102
|
+
visit.id = visits[visitIndex].id;
|
|
103
|
+
visit.hits = visits[visitIndex].hits + 1;
|
|
104
|
+
visits[visitIndex] = visit;
|
|
105
|
+
} else {
|
|
106
|
+
visits.push(visit);
|
|
107
|
+
}
|
|
108
|
+
visits.sort((a, b) => b.timestamp - a.timestamp);
|
|
109
|
+
await this.persistAll(visits.splice(0, this.limit));
|
|
110
|
+
return visit;
|
|
111
|
+
}
|
|
112
|
+
async persistAll(visits) {
|
|
113
|
+
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
|
114
|
+
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
|
115
|
+
return this.storageApi.set(storageKey, visits);
|
|
116
|
+
}
|
|
117
|
+
async retrieveAll() {
|
|
118
|
+
var _a;
|
|
119
|
+
const { userEntityRef } = await this.identityApi.getBackstageIdentity();
|
|
120
|
+
const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`;
|
|
121
|
+
let visits;
|
|
122
|
+
try {
|
|
123
|
+
visits = (_a = this.storageApi.snapshot(storageKey).value) != null ? _a : [];
|
|
124
|
+
} catch {
|
|
125
|
+
visits = [];
|
|
126
|
+
}
|
|
127
|
+
return visits;
|
|
128
|
+
}
|
|
129
|
+
// This assumes Visit fields are either numbers or strings
|
|
130
|
+
compare(order, a, b) {
|
|
131
|
+
const isNumber = typeof a[order.field] === "number";
|
|
132
|
+
return isNumber ? a[order.field] - b[order.field] : `${a[order.field]}`.localeCompare(`${b[order.field]}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
class VisitsWebStorageApi {
|
|
137
|
+
static create(options) {
|
|
138
|
+
return VisitsStorageApi.create({
|
|
139
|
+
limit: options.limit,
|
|
140
|
+
identityApi: options.identityApi,
|
|
141
|
+
storageApi: WebStorage.create({ errorApi: options.errorApi })
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const visitsApiRef = createApiRef({
|
|
147
|
+
id: "homepage.visits"
|
|
148
|
+
});
|
|
149
|
+
|
|
32
150
|
const homePlugin = createPlugin({
|
|
33
151
|
id: "home",
|
|
152
|
+
apis: [
|
|
153
|
+
createApiFactory({
|
|
154
|
+
api: visitsApiRef,
|
|
155
|
+
deps: {
|
|
156
|
+
storageApi: storageApiRef,
|
|
157
|
+
identityApi: identityApiRef
|
|
158
|
+
},
|
|
159
|
+
factory: ({ storageApi, identityApi }) => VisitsStorageApi.create({ storageApi, identityApi })
|
|
160
|
+
})
|
|
161
|
+
],
|
|
34
162
|
routes: {
|
|
35
163
|
root: rootRouteRef
|
|
36
164
|
}
|
|
@@ -38,7 +166,7 @@ const homePlugin = createPlugin({
|
|
|
38
166
|
const HomepageCompositionRoot = homePlugin.provide(
|
|
39
167
|
createRoutableExtension({
|
|
40
168
|
name: "HomepageCompositionRoot",
|
|
41
|
-
component: () => import('./esm/index-
|
|
169
|
+
component: () => import('./esm/index-20932a73.esm.js').then((m) => m.HomepageCompositionRoot),
|
|
42
170
|
mountPoint: rootRouteRef
|
|
43
171
|
})
|
|
44
172
|
);
|
|
@@ -132,6 +260,18 @@ const HeaderWorldClock = homePlugin.provide(
|
|
|
132
260
|
}
|
|
133
261
|
})
|
|
134
262
|
);
|
|
263
|
+
const HomePageTopVisited = homePlugin.provide(
|
|
264
|
+
createCardExtension$1({
|
|
265
|
+
name: "HomePageTopVisited",
|
|
266
|
+
components: () => import('./esm/TopVisited-918c6b5f.esm.js')
|
|
267
|
+
})
|
|
268
|
+
);
|
|
269
|
+
const HomePageRecentlyVisited = homePlugin.provide(
|
|
270
|
+
createCardExtension$1({
|
|
271
|
+
name: "HomePageRecentlyVisited",
|
|
272
|
+
components: () => import('./esm/RecentlyVisited-618bf2be.esm.js')
|
|
273
|
+
})
|
|
274
|
+
);
|
|
135
275
|
|
|
136
276
|
const Form = withTheme(require("@rjsf/material-ui-v5").Theme);
|
|
137
277
|
const useStyles$3 = makeStyles(
|
|
@@ -658,6 +798,59 @@ const CustomHomepageGrid = (props) => {
|
|
|
658
798
|
));
|
|
659
799
|
};
|
|
660
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
|
+
|
|
661
854
|
const TemplateBackstageLogo = (props) => {
|
|
662
855
|
return /* @__PURE__ */ React.createElement(
|
|
663
856
|
"svg",
|
|
@@ -707,5 +900,5 @@ const TemplateBackstageLogoIcon = () => {
|
|
|
707
900
|
const createCardExtension = createCardExtension$1;
|
|
708
901
|
const SettingsModal = SettingsModal$1;
|
|
709
902
|
|
|
710
|
-
export { ComponentAccordion, ComponentTab, ComponentTabs, CustomHomepageGrid, HeaderWorldClock, HomePageCompanyLogo, HomePageRandomJoke, HomePageStarredEntities, HomePageToolkit, HomepageCompositionRoot, SettingsModal, TemplateBackstageLogo, TemplateBackstageLogoIcon, WelcomeTitle, createCardExtension, homePlugin };
|
|
903
|
+
export { ComponentAccordion, ComponentTab, ComponentTabs, CustomHomepageGrid, HeaderWorldClock, HomePageCompanyLogo, HomePageRandomJoke, HomePageRecentlyVisited, HomePageStarredEntities, HomePageToolkit, HomePageTopVisited, HomepageCompositionRoot, SettingsModal, TemplateBackstageLogo, TemplateBackstageLogoIcon, VisitListener, VisitsStorageApi, VisitsWebStorageApi, WelcomeTitle, createCardExtension, homePlugin, visitsApiRef };
|
|
711
904
|
//# sourceMappingURL=index.esm.js.map
|