@backstage/frontend-plugin-api 0.14.0-next.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/dist/analytics/useAnalytics.esm.js +1 -0
- package/dist/analytics/useAnalytics.esm.js.map +1 -1
- package/dist/apis/definitions/IconsApi.esm.js.map +1 -1
- package/dist/apis/definitions/PluginHeaderActionsApi.esm.js +11 -0
- package/dist/apis/definitions/PluginHeaderActionsApi.esm.js.map +1 -0
- package/dist/blueprints/PageBlueprint.esm.js +97 -9
- package/dist/blueprints/PageBlueprint.esm.js.map +1 -1
- package/dist/blueprints/PluginHeaderActionBlueprint.esm.js +52 -0
- package/dist/blueprints/PluginHeaderActionBlueprint.esm.js.map +1 -0
- package/dist/blueprints/SubPageBlueprint.esm.js +66 -0
- package/dist/blueprints/SubPageBlueprint.esm.js.map +1 -0
- package/dist/components/ExtensionBoundary.esm.js +1 -0
- package/dist/components/ExtensionBoundary.esm.js.map +1 -1
- package/dist/components/PageLayout.esm.js +99 -0
- package/dist/components/PageLayout.esm.js.map +1 -0
- package/dist/components/createSwappableComponent.esm.js +1 -0
- package/dist/components/createSwappableComponent.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/InternalFrontendPlugin.esm.js.map +1 -1
- package/dist/index.d.ts +242 -25
- package/dist/index.esm.js +4 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/routing/useRouteRef.esm.js +1 -0
- package/dist/routing/useRouteRef.esm.js.map +1 -1
- package/dist/translation/useTranslationRef.esm.js +1 -0
- package/dist/translation/useTranslationRef.esm.js.map +1 -1
- package/dist/wiring/coreExtensionData.esm.js +2 -0
- package/dist/wiring/coreExtensionData.esm.js.map +1 -1
- package/dist/wiring/createFrontendPlugin.esm.js +7 -2
- package/dist/wiring/createFrontendPlugin.esm.js.map +1 -1
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# @backstage/frontend-plugin-api
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ef6916e: Added `IconElement` type as a replacement for the deprecated `IconComponent`. The `IconsApi` now has a new `icon()` method that returns `IconElement`, while the existing `getIcon()` method is deprecated. The `IconBundleBlueprint` now accepts both `IconComponent` and `IconElement` values.
|
|
8
|
+
- bb9b471: Plugin IDs that do not match the standard format are deprecated (letters, digits, and dashes only, starting with a letter). Plugin IDs that do no match this format will be rejected in a future release.
|
|
9
|
+
- ef6916e: Added `SubPageBlueprint` for creating sub-page tabs, `PluginHeaderActionBlueprint` and `PluginHeaderActionsApi` for plugin-scoped header actions, and `PageLayout` as a swappable component. The `PageBlueprint` now supports sub-pages with tabbed navigation, page title, icon, and header actions. Plugins can now specify a `title` and `icon` in `createFrontendPlugin`.
|
|
10
|
+
- c38b74d: **BREAKING**: The following blueprints have been removed and are now only available from `@backstage/plugin-app-react`:
|
|
11
|
+
|
|
12
|
+
- `IconBundleBlueprint`
|
|
13
|
+
- `NavContentBlueprint`
|
|
14
|
+
- `RouterBlueprint`
|
|
15
|
+
- `SignInPageBlueprint`
|
|
16
|
+
- `SwappableComponentBlueprint`
|
|
17
|
+
- `ThemeBlueprint`
|
|
18
|
+
- `TranslationBlueprint`
|
|
19
|
+
|
|
20
|
+
- 10ebed4: **BREAKING**: Removed type support for multiple attachment points in the `ExtensionDefinitionAttachTo` type. Extensions can no longer specify an array of attachment points in the `attachTo` property.
|
|
21
|
+
|
|
22
|
+
The runtime still supports multiple attachment points for backward compatibility with existing compiled code, but new code will receive type errors if attempting to use this pattern.
|
|
23
|
+
|
|
24
|
+
Extensions that previously used multiple attachment points should migrate to using a Utility API pattern instead. See the [Sharing Extensions Across Multiple Locations](https://backstage.io/docs/frontend-system/architecture/27-sharing-extensions) guide for the recommended approach.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- 7edb810: Added a new `internal` option to `createExtensionInput` that marks the input as only allowing attachments from the same plugin.
|
|
29
|
+
- 9554c36: **DEPRECATED**: Multiple attachment points for extensions have been deprecated. The functionality continues to work for backward compatibility, but will log a deprecation warning and be removed in a future release.
|
|
30
|
+
|
|
31
|
+
Extensions using array attachment points should migrate to using Utility APIs instead. See the [Sharing Extensions Across Multiple Locations](https://backstage.io/docs/frontend-system/architecture/27-sharing-extensions) guide for the recommended pattern.
|
|
32
|
+
|
|
33
|
+
- 53b6549: Plugins in the new frontend system now have a `pluginId` field rather than `id` to better align with naming conventions used throughout the frontend and backend systems. The old field is still present but marked as deprecated. All internal code has been updated to prefer `pluginId` while maintaining backward compatibility by falling back to `id` when needed.
|
|
34
|
+
- a7e0d50: Updated `react-router-dom` peer dependency to `^6.30.2` and explicitly disabled v7 future flags to suppress deprecation warnings.
|
|
35
|
+
- 69d880e: Bump to latest zod to ensure it has the latest features
|
|
36
|
+
- Updated dependencies
|
|
37
|
+
- @backstage/version-bridge@1.0.12
|
|
38
|
+
|
|
39
|
+
## 0.14.0-next.2
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- a7e0d50: Prepare for React Router v7 migration by updating to v6.30.2 across all NFS packages and enabling v7 future flags. Convert routes from splat paths to parent/child structure with Outlet components.
|
|
44
|
+
- Updated dependencies
|
|
45
|
+
- @backstage/version-bridge@1.0.12-next.0
|
|
46
|
+
|
|
3
47
|
## 0.14.0-next.1
|
|
4
48
|
|
|
5
49
|
### Minor Changes
|
|
@@ -19,6 +19,7 @@ import '../apis/definitions/RouteResolutionApi.esm.js';
|
|
|
19
19
|
import '../apis/definitions/StorageApi.esm.js';
|
|
20
20
|
import { analyticsApiRef } from '../apis/definitions/AnalyticsApi.esm.js';
|
|
21
21
|
import '../apis/definitions/TranslationApi.esm.js';
|
|
22
|
+
import '../apis/definitions/PluginHeaderActionsApi.esm.js';
|
|
22
23
|
import { useRef } from 'react';
|
|
23
24
|
import { Tracker } from './Tracker.esm.js';
|
|
24
25
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAnalytics.esm.js","sources":["../../src/analytics/useAnalytics.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 { useApi } from '../apis/system';\nimport { useAnalyticsContext } from './AnalyticsContext';\nimport { analyticsApiRef, AnalyticsTracker, AnalyticsApi } from '../apis';\nimport { useRef } from 'react';\nimport { Tracker } from './Tracker';\n\nfunction useAnalyticsApi(): AnalyticsApi {\n try {\n return useApi(analyticsApiRef);\n } catch (error) {\n if (error.name === 'NotImplementedError') {\n return { captureEvent: () => {} };\n }\n throw error;\n }\n}\n\n/**\n * Gets a pre-configured analytics tracker.\n *\n * @public\n */\nexport function useAnalytics(): AnalyticsTracker {\n const trackerRef = useRef<Tracker | null>(null);\n const context = useAnalyticsContext();\n // Our goal is to make this API truly optional for any/all consuming code\n // (including tests). This hook runs last to ensure hook order is, as much as\n // possible, maintained.\n const analyticsApi = useAnalyticsApi();\n\n function getTracker(): Tracker {\n if (trackerRef.current === null) {\n trackerRef.current = new Tracker(analyticsApi);\n }\n return trackerRef.current;\n }\n\n const tracker = getTracker();\n // this is not ideal, but it allows to memoize the tracker\n // without explicitly set the context as dependency.\n tracker.setContext(context);\n\n return tracker;\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useAnalytics.esm.js","sources":["../../src/analytics/useAnalytics.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 { useApi } from '../apis/system';\nimport { useAnalyticsContext } from './AnalyticsContext';\nimport { analyticsApiRef, AnalyticsTracker, AnalyticsApi } from '../apis';\nimport { useRef } from 'react';\nimport { Tracker } from './Tracker';\n\nfunction useAnalyticsApi(): AnalyticsApi {\n try {\n return useApi(analyticsApiRef);\n } catch (error) {\n if (error.name === 'NotImplementedError') {\n return { captureEvent: () => {} };\n }\n throw error;\n }\n}\n\n/**\n * Gets a pre-configured analytics tracker.\n *\n * @public\n */\nexport function useAnalytics(): AnalyticsTracker {\n const trackerRef = useRef<Tracker | null>(null);\n const context = useAnalyticsContext();\n // Our goal is to make this API truly optional for any/all consuming code\n // (including tests). This hook runs last to ensure hook order is, as much as\n // possible, maintained.\n const analyticsApi = useAnalyticsApi();\n\n function getTracker(): Tracker {\n if (trackerRef.current === null) {\n trackerRef.current = new Tracker(analyticsApi);\n }\n return trackerRef.current;\n }\n\n const tracker = getTracker();\n // this is not ideal, but it allows to memoize the tracker\n // without explicitly set the context as dependency.\n tracker.setContext(context);\n\n return tracker;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAS,eAAA,GAAgC;AACvC,EAAA,IAAI;AACF,IAAA,OAAO,OAAO,eAAe,CAAA;AAAA,EAC/B,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,KAAA,CAAM,SAAS,qBAAA,EAAuB;AACxC,MAAA,OAAO,EAAE,cAAc,MAAM;AAAA,MAAC,CAAA,EAAE;AAAA,IAClC;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF;AAOO,SAAS,YAAA,GAAiC;AAC/C,EAAA,MAAM,UAAA,GAAa,OAAuB,IAAI,CAAA;AAC9C,EAAA,MAAM,UAAU,mBAAA,EAAoB;AAIpC,EAAA,MAAM,eAAe,eAAA,EAAgB;AAErC,EAAA,SAAS,UAAA,GAAsB;AAC7B,IAAA,IAAI,UAAA,CAAW,YAAY,IAAA,EAAM;AAC/B,MAAA,UAAA,CAAW,OAAA,GAAU,IAAI,OAAA,CAAQ,YAAY,CAAA;AAAA,IAC/C;AACA,IAAA,OAAO,UAAA,CAAW,OAAA;AAAA,EACpB;AAEA,EAAA,MAAM,UAAU,UAAA,EAAW;AAG3B,EAAA,OAAA,CAAQ,WAAW,OAAO,CAAA;AAE1B,EAAA,OAAO,OAAA;AACT;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconsApi.esm.js","sources":["../../../src/apis/definitions/IconsApi.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { createApiRef } from '../system';\nimport { IconComponent } from '../../icons';\n\n/**\n * API for accessing app icons.\n *\n * @public\n */\nexport interface IconsApi {\n getIcon(key: string): IconComponent | undefined;\n\n listIconKeys(): string[];\n}\n\n/**\n * The `ApiRef` of {@link IconsApi}.\n *\n * @public\n */\nexport const iconsApiRef = createApiRef<IconsApi>({\n id: 'core.icons',\n});\n"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"IconsApi.esm.js","sources":["../../../src/apis/definitions/IconsApi.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { createApiRef } from '../system';\nimport { IconComponent, IconElement } from '../../icons';\n\n/**\n * API for accessing app icons.\n *\n * @public\n */\nexport interface IconsApi {\n /**\n * Look up an icon element by key.\n */\n icon(key: string): IconElement | undefined;\n\n /**\n * @deprecated Use {@link IconsApi.icon} instead.\n */\n getIcon(key: string): IconComponent | undefined;\n\n listIconKeys(): string[];\n}\n\n/**\n * The `ApiRef` of {@link IconsApi}.\n *\n * @public\n */\nexport const iconsApiRef = createApiRef<IconsApi>({\n id: 'core.icons',\n});\n"],"names":[],"mappings":";;;;;AA2CO,MAAM,cAAc,YAAA,CAAuB;AAAA,EAChD,EAAA,EAAI;AACN,CAAC;;;;"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import 'react/jsx-runtime';
|
|
2
|
+
import '@backstage/version-bridge';
|
|
3
|
+
import '@backstage/errors';
|
|
4
|
+
import { createApiRef } from '../system/ApiRef.esm.js';
|
|
5
|
+
|
|
6
|
+
const pluginHeaderActionsApiRef = createApiRef({
|
|
7
|
+
id: "core.plugin-header-actions"
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export { pluginHeaderActionsApiRef };
|
|
11
|
+
//# sourceMappingURL=PluginHeaderActionsApi.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginHeaderActionsApi.esm.js","sources":["../../../src/apis/definitions/PluginHeaderActionsApi.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { JSX } from 'react';\nimport { createApiRef } from '../system';\n\n/**\n * API for retrieving plugin-scoped header actions.\n *\n * @remarks\n *\n * Header actions are provided via\n * {@link @backstage/frontend-plugin-api#PluginHeaderActionBlueprint}\n * and automatically scoped to the providing plugin.\n *\n * @public\n */\nexport type PluginHeaderActionsApi = {\n /**\n * Returns the header actions for a given plugin.\n */\n getPluginHeaderActions(pluginId: string): Array<JSX.Element | null>;\n};\n\n/**\n * The `ApiRef` of {@link PluginHeaderActionsApi}.\n *\n * @public\n */\nexport const pluginHeaderActionsApiRef = createApiRef<PluginHeaderActionsApi>({\n id: 'core.plugin-header-actions',\n});\n"],"names":[],"mappings":";;;;;AA0CO,MAAM,4BAA4B,YAAA,CAAqC;AAAA,EAC5E,EAAA,EAAI;AACN,CAAC;;;;"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { Routes, Route, Navigate } from 'react-router-dom';
|
|
1
3
|
import { coreExtensionData } from '../wiring/coreExtensionData.esm.js';
|
|
2
4
|
import 'zod';
|
|
3
5
|
import 'zod-to-json-schema';
|
|
6
|
+
import { createExtensionInput } from '../wiring/createExtensionInput.esm.js';
|
|
4
7
|
import { createExtensionBlueprint } from '../wiring/createExtensionBlueprint.esm.js';
|
|
5
8
|
import { ExtensionBoundary } from '../components/ExtensionBoundary.esm.js';
|
|
6
|
-
import 'react/jsx-runtime';
|
|
7
9
|
import '../apis/definitions/AppTreeApi.esm.js';
|
|
8
10
|
import '../apis/definitions/auth.esm.js';
|
|
9
11
|
import '../apis/definitions/AlertApi.esm.js';
|
|
@@ -23,33 +25,119 @@ import '../apis/definitions/RouteResolutionApi.esm.js';
|
|
|
23
25
|
import '../apis/definitions/StorageApi.esm.js';
|
|
24
26
|
import '../apis/definitions/AnalyticsApi.esm.js';
|
|
25
27
|
import '../apis/definitions/TranslationApi.esm.js';
|
|
26
|
-
import '
|
|
27
|
-
import '
|
|
28
|
+
import { pluginHeaderActionsApiRef } from '../apis/definitions/PluginHeaderActionsApi.esm.js';
|
|
29
|
+
import { useApi } from '../apis/system/useApi.esm.js';
|
|
28
30
|
import 'react';
|
|
29
31
|
import '../components/AppNodeProvider.esm.js';
|
|
30
32
|
import '../components/DefaultSwappableComponents.esm.js';
|
|
33
|
+
import { PageLayout } from '../components/PageLayout.esm.js';
|
|
31
34
|
|
|
32
35
|
const PageBlueprint = createExtensionBlueprint({
|
|
33
36
|
kind: "page",
|
|
34
37
|
attachTo: { id: "app/routes", input: "routes" },
|
|
38
|
+
inputs: {
|
|
39
|
+
pages: createExtensionInput([
|
|
40
|
+
coreExtensionData.routePath,
|
|
41
|
+
coreExtensionData.routeRef.optional(),
|
|
42
|
+
coreExtensionData.reactElement,
|
|
43
|
+
coreExtensionData.title.optional(),
|
|
44
|
+
coreExtensionData.icon.optional()
|
|
45
|
+
])
|
|
46
|
+
},
|
|
35
47
|
output: [
|
|
36
48
|
coreExtensionData.routePath,
|
|
37
49
|
coreExtensionData.reactElement,
|
|
38
|
-
coreExtensionData.routeRef.optional()
|
|
50
|
+
coreExtensionData.routeRef.optional(),
|
|
51
|
+
coreExtensionData.title.optional(),
|
|
52
|
+
coreExtensionData.icon.optional()
|
|
39
53
|
],
|
|
40
54
|
config: {
|
|
41
55
|
schema: {
|
|
42
|
-
path: (z) => z.string().optional()
|
|
56
|
+
path: (z) => z.string().optional(),
|
|
57
|
+
title: (z) => z.string().optional()
|
|
43
58
|
}
|
|
44
59
|
},
|
|
45
|
-
*factory(params, { config, node }) {
|
|
60
|
+
*factory(params, { config, node, inputs }) {
|
|
61
|
+
const title = config.title ?? params.title;
|
|
62
|
+
const icon = params.icon;
|
|
63
|
+
const pluginId = node.spec.plugin.pluginId;
|
|
64
|
+
const noHeader = params.noHeader ?? false;
|
|
46
65
|
yield coreExtensionData.routePath(config.path ?? params.path);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
if (params.loader) {
|
|
67
|
+
const loader = params.loader;
|
|
68
|
+
const PageContent = () => {
|
|
69
|
+
const headerActionsApi = useApi(pluginHeaderActionsApiRef);
|
|
70
|
+
const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);
|
|
71
|
+
return /* @__PURE__ */ jsx(
|
|
72
|
+
PageLayout,
|
|
73
|
+
{
|
|
74
|
+
title: title ?? node.spec.plugin.title ?? node.spec.plugin.pluginId,
|
|
75
|
+
icon: icon ?? node.spec.plugin.icon,
|
|
76
|
+
noHeader,
|
|
77
|
+
headerActions,
|
|
78
|
+
children: ExtensionBoundary.lazy(node, loader)
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
yield coreExtensionData.reactElement(/* @__PURE__ */ jsx(PageContent, {}));
|
|
83
|
+
} else if (inputs.pages.length > 0) {
|
|
84
|
+
const tabs = inputs.pages.map((page) => {
|
|
85
|
+
const path = page.get(coreExtensionData.routePath);
|
|
86
|
+
const tabTitle = page.get(coreExtensionData.title);
|
|
87
|
+
const tabIcon = page.get(coreExtensionData.icon);
|
|
88
|
+
return {
|
|
89
|
+
id: path,
|
|
90
|
+
label: tabTitle || path,
|
|
91
|
+
icon: tabIcon,
|
|
92
|
+
href: path
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
const PageContent = () => {
|
|
96
|
+
const firstPagePath = inputs.pages[0]?.get(coreExtensionData.routePath);
|
|
97
|
+
const headerActionsApi = useApi(pluginHeaderActionsApiRef);
|
|
98
|
+
const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);
|
|
99
|
+
return /* @__PURE__ */ jsx(
|
|
100
|
+
PageLayout,
|
|
101
|
+
{
|
|
102
|
+
title,
|
|
103
|
+
icon,
|
|
104
|
+
tabs,
|
|
105
|
+
headerActions,
|
|
106
|
+
children: /* @__PURE__ */ jsxs(Routes, { children: [
|
|
107
|
+
firstPagePath && /* @__PURE__ */ jsx(
|
|
108
|
+
Route,
|
|
109
|
+
{
|
|
110
|
+
index: true,
|
|
111
|
+
element: /* @__PURE__ */ jsx(Navigate, { to: firstPagePath, replace: true })
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
inputs.pages.map((page, index) => {
|
|
115
|
+
const path = page.get(coreExtensionData.routePath);
|
|
116
|
+
const element = page.get(coreExtensionData.reactElement);
|
|
117
|
+
return /* @__PURE__ */ jsx(Route, { path: `${path}/*`, element }, index);
|
|
118
|
+
})
|
|
119
|
+
] })
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
yield coreExtensionData.reactElement(/* @__PURE__ */ jsx(PageContent, {}));
|
|
124
|
+
} else {
|
|
125
|
+
const PageContent = () => {
|
|
126
|
+
const headerActionsApi = useApi(pluginHeaderActionsApiRef);
|
|
127
|
+
const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);
|
|
128
|
+
return /* @__PURE__ */ jsx(PageLayout, { title, icon, headerActions });
|
|
129
|
+
};
|
|
130
|
+
yield coreExtensionData.reactElement(/* @__PURE__ */ jsx(PageContent, {}));
|
|
131
|
+
}
|
|
50
132
|
if (params.routeRef) {
|
|
51
133
|
yield coreExtensionData.routeRef(params.routeRef);
|
|
52
134
|
}
|
|
135
|
+
if (title) {
|
|
136
|
+
yield coreExtensionData.title(title);
|
|
137
|
+
}
|
|
138
|
+
if (icon) {
|
|
139
|
+
yield coreExtensionData.icon(icon);
|
|
140
|
+
}
|
|
53
141
|
}
|
|
54
142
|
});
|
|
55
143
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageBlueprint.esm.js","sources":["../../src/blueprints/PageBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2024 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 { RouteRef } from '../routing';\nimport {
|
|
1
|
+
{"version":3,"file":"PageBlueprint.esm.js","sources":["../../src/blueprints/PageBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2024 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 { JSX } from 'react';\nimport { Routes, Route, Navigate } from 'react-router-dom';\nimport { IconElement } from '../icons/types';\nimport { RouteRef } from '../routing';\nimport {\n coreExtensionData,\n createExtensionBlueprint,\n createExtensionInput,\n} from '../wiring';\nimport { ExtensionBoundary, PageLayout, PageTab } from '../components';\nimport { useApi } from '../apis/system';\nimport { pluginHeaderActionsApiRef } from '../apis/definitions/PluginHeaderActionsApi';\n\n/**\n * Creates extensions that are routable React page components.\n *\n * @public\n */\nexport const PageBlueprint = createExtensionBlueprint({\n kind: 'page',\n attachTo: { id: 'app/routes', input: 'routes' },\n inputs: {\n pages: createExtensionInput([\n coreExtensionData.routePath,\n coreExtensionData.routeRef.optional(),\n coreExtensionData.reactElement,\n coreExtensionData.title.optional(),\n coreExtensionData.icon.optional(),\n ]),\n },\n output: [\n coreExtensionData.routePath,\n coreExtensionData.reactElement,\n coreExtensionData.routeRef.optional(),\n coreExtensionData.title.optional(),\n coreExtensionData.icon.optional(),\n ],\n config: {\n schema: {\n path: z => z.string().optional(),\n title: z => z.string().optional(),\n },\n },\n *factory(\n params: {\n /**\n * @deprecated Use the `path` param instead.\n */\n defaultPath?: [Error: `Use the 'path' param instead`];\n path: string;\n title?: string;\n icon?: IconElement;\n loader?: () => Promise<JSX.Element>;\n routeRef?: RouteRef;\n /**\n * Hide the default plugin page header, making the page fill up all available space.\n */\n noHeader?: boolean;\n },\n { config, node, inputs },\n ) {\n const title = config.title ?? params.title;\n const icon = params.icon;\n const pluginId = node.spec.plugin.pluginId;\n const noHeader = params.noHeader ?? false;\n\n yield coreExtensionData.routePath(config.path ?? params.path);\n if (params.loader) {\n const loader = params.loader;\n const PageContent = () => {\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n\n return (\n <PageLayout\n title={title ?? node.spec.plugin.title ?? node.spec.plugin.pluginId}\n icon={icon ?? node.spec.plugin.icon}\n noHeader={noHeader}\n headerActions={headerActions}\n >\n {ExtensionBoundary.lazy(node, loader)}\n </PageLayout>\n );\n };\n yield coreExtensionData.reactElement(<PageContent />);\n } else if (inputs.pages.length > 0) {\n // Parent page with sub-pages - render header with tabs\n const tabs: PageTab[] = inputs.pages.map(page => {\n const path = page.get(coreExtensionData.routePath);\n const tabTitle = page.get(coreExtensionData.title);\n const tabIcon = page.get(coreExtensionData.icon);\n return {\n id: path,\n label: tabTitle || path,\n icon: tabIcon,\n href: path,\n };\n });\n\n const PageContent = () => {\n const firstPagePath = inputs.pages[0]?.get(coreExtensionData.routePath);\n\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n\n return (\n <PageLayout\n title={title}\n icon={icon}\n tabs={tabs}\n headerActions={headerActions}\n >\n <Routes>\n {firstPagePath && (\n <Route\n index\n element={<Navigate to={firstPagePath} replace />}\n />\n )}\n {inputs.pages.map((page, index) => {\n const path = page.get(coreExtensionData.routePath);\n const element = page.get(coreExtensionData.reactElement);\n return (\n <Route key={index} path={`${path}/*`} element={element} />\n );\n })}\n </Routes>\n </PageLayout>\n );\n };\n\n yield coreExtensionData.reactElement(<PageContent />);\n } else {\n const PageContent = () => {\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n return (\n <PageLayout title={title} icon={icon} headerActions={headerActions} />\n );\n };\n yield coreExtensionData.reactElement(<PageContent />);\n }\n if (params.routeRef) {\n yield coreExtensionData.routeRef(params.routeRef);\n }\n if (title) {\n yield coreExtensionData.title(title);\n }\n if (icon) {\n yield coreExtensionData.icon(icon);\n }\n },\n});\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCO,MAAM,gBAAgB,wBAAA,CAAyB;AAAA,EACpD,IAAA,EAAM,MAAA;AAAA,EACN,QAAA,EAAU,EAAE,EAAA,EAAI,YAAA,EAAc,OAAO,QAAA,EAAS;AAAA,EAC9C,MAAA,EAAQ;AAAA,IACN,OAAO,oBAAA,CAAqB;AAAA,MAC1B,iBAAA,CAAkB,SAAA;AAAA,MAClB,iBAAA,CAAkB,SAAS,QAAA,EAAS;AAAA,MACpC,iBAAA,CAAkB,YAAA;AAAA,MAClB,iBAAA,CAAkB,MAAM,QAAA,EAAS;AAAA,MACjC,iBAAA,CAAkB,KAAK,QAAA;AAAS,KACjC;AAAA,GACH;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,iBAAA,CAAkB,SAAA;AAAA,IAClB,iBAAA,CAAkB,YAAA;AAAA,IAClB,iBAAA,CAAkB,SAAS,QAAA,EAAS;AAAA,IACpC,iBAAA,CAAkB,MAAM,QAAA,EAAS;AAAA,IACjC,iBAAA,CAAkB,KAAK,QAAA;AAAS,GAClC;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS;AAAA,MAC/B,KAAA,EAAO,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,GAAS,QAAA;AAAS;AAClC,GACF;AAAA,EACA,CAAC,OAAA,CACC,MAAA,EAeA,EAAE,MAAA,EAAQ,IAAA,EAAM,QAAO,EACvB;AACA,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,IAAS,MAAA,CAAO,KAAA;AACrC,IAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,QAAA;AAClC,IAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,KAAA;AAEpC,IAAA,MAAM,iBAAA,CAAkB,SAAA,CAAU,MAAA,CAAO,IAAA,IAAQ,OAAO,IAAI,CAAA;AAC5D,IAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,MAAA,MAAM,SAAS,MAAA,CAAO,MAAA;AACtB,MAAA,MAAM,cAAc,MAAM;AACxB,QAAA,MAAM,gBAAA,GAAmB,OAAO,yBAAyB,CAAA;AACzD,QAAA,MAAM,aAAA,GAAgB,gBAAA,CAAiB,sBAAA,CAAuB,QAAQ,CAAA;AAEtE,QAAA,uBACE,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO,SAAS,IAAA,CAAK,IAAA,CAAK,OAAO,KAAA,IAAS,IAAA,CAAK,KAAK,MAAA,CAAO,QAAA;AAAA,YAC3D,IAAA,EAAM,IAAA,IAAQ,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,YAC/B,QAAA;AAAA,YACA,aAAA;AAAA,YAEC,QAAA,EAAA,iBAAA,CAAkB,IAAA,CAAK,IAAA,EAAM,MAAM;AAAA;AAAA,SACtC;AAAA,MAEJ,CAAA;AACA,MAAA,MAAM,iBAAA,CAAkB,YAAA,iBAAa,GAAA,CAAC,WAAA,EAAA,EAAY,CAAE,CAAA;AAAA,IACtD,CAAA,MAAA,IAAW,MAAA,CAAO,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AAElC,MAAA,MAAM,IAAA,GAAkB,MAAA,CAAO,KAAA,CAAM,GAAA,CAAI,CAAA,IAAA,KAAQ;AAC/C,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,iBAAA,CAAkB,SAAS,CAAA;AACjD,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,iBAAA,CAAkB,KAAK,CAAA;AACjD,QAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,iBAAA,CAAkB,IAAI,CAAA;AAC/C,QAAA,OAAO;AAAA,UACL,EAAA,EAAI,IAAA;AAAA,UACJ,OAAO,QAAA,IAAY,IAAA;AAAA,UACnB,IAAA,EAAM,OAAA;AAAA,UACN,IAAA,EAAM;AAAA,SACR;AAAA,MACF,CAAC,CAAA;AAED,MAAA,MAAM,cAAc,MAAM;AACxB,QAAA,MAAM,gBAAgB,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA,EAAG,GAAA,CAAI,kBAAkB,SAAS,CAAA;AAEtE,QAAA,MAAM,gBAAA,GAAmB,OAAO,yBAAyB,CAAA;AACzD,QAAA,MAAM,aAAA,GAAgB,gBAAA,CAAiB,sBAAA,CAAuB,QAAQ,CAAA;AAEtE,QAAA,uBACE,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,KAAA;AAAA,YACA,IAAA;AAAA,YACA,IAAA;AAAA,YACA,aAAA;AAAA,YAEA,+BAAC,MAAA,EAAA,EACE,QAAA,EAAA;AAAA,cAAA,aAAA,oBACC,GAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAK,IAAA;AAAA,kBACL,yBAAS,GAAA,CAAC,QAAA,EAAA,EAAS,EAAA,EAAI,aAAA,EAAe,SAAO,IAAA,EAAC;AAAA;AAAA,eAChD;AAAA,cAED,MAAA,CAAO,KAAA,CAAM,GAAA,CAAI,CAAC,MAAM,KAAA,KAAU;AACjC,gBAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,iBAAA,CAAkB,SAAS,CAAA;AACjD,gBAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,iBAAA,CAAkB,YAAY,CAAA;AACvD,gBAAA,2BACG,KAAA,EAAA,EAAkB,IAAA,EAAM,GAAG,IAAI,CAAA,EAAA,CAAA,EAAM,WAA1B,KAA4C,CAAA;AAAA,cAE5D,CAAC;AAAA,aAAA,EACH;AAAA;AAAA,SACF;AAAA,MAEJ,CAAA;AAEA,MAAA,MAAM,iBAAA,CAAkB,YAAA,iBAAa,GAAA,CAAC,WAAA,EAAA,EAAY,CAAE,CAAA;AAAA,IACtD,CAAA,MAAO;AACL,MAAA,MAAM,cAAc,MAAM;AACxB,QAAA,MAAM,gBAAA,GAAmB,OAAO,yBAAyB,CAAA;AACzD,QAAA,MAAM,aAAA,GAAgB,gBAAA,CAAiB,sBAAA,CAAuB,QAAQ,CAAA;AACtE,QAAA,uBACE,GAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAc,IAAA,EAAY,aAAA,EAA8B,CAAA;AAAA,MAExE,CAAA;AACA,MAAA,MAAM,iBAAA,CAAkB,YAAA,iBAAa,GAAA,CAAC,WAAA,EAAA,EAAY,CAAE,CAAA;AAAA,IACtD;AACA,IAAA,IAAI,OAAO,QAAA,EAAU;AACnB,MAAA,MAAM,iBAAA,CAAkB,QAAA,CAAS,MAAA,CAAO,QAAQ,CAAA;AAAA,IAClD;AACA,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAM,iBAAA,CAAkB,MAAM,KAAK,CAAA;AAAA,IACrC;AACA,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,MAAM,iBAAA,CAAkB,KAAK,IAAI,CAAA;AAAA,IACnC;AAAA,EACF;AACF,CAAC;;;;"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { lazy } from 'react';
|
|
3
|
+
import { ExtensionBoundary } from '../components/ExtensionBoundary.esm.js';
|
|
4
|
+
import '../apis/definitions/AppTreeApi.esm.js';
|
|
5
|
+
import '../apis/definitions/auth.esm.js';
|
|
6
|
+
import '../apis/definitions/AlertApi.esm.js';
|
|
7
|
+
import '../apis/definitions/AppLanguageApi.esm.js';
|
|
8
|
+
import '../apis/definitions/AppThemeApi.esm.js';
|
|
9
|
+
import '../apis/definitions/SwappableComponentsApi.esm.js';
|
|
10
|
+
import '../apis/definitions/ConfigApi.esm.js';
|
|
11
|
+
import '../apis/definitions/DiscoveryApi.esm.js';
|
|
12
|
+
import '../apis/definitions/ErrorApi.esm.js';
|
|
13
|
+
import '../apis/definitions/FeatureFlagsApi.esm.js';
|
|
14
|
+
import '../apis/definitions/FetchApi.esm.js';
|
|
15
|
+
import '../apis/definitions/IconsApi.esm.js';
|
|
16
|
+
import '../apis/definitions/IdentityApi.esm.js';
|
|
17
|
+
import '../apis/definitions/DialogApi.esm.js';
|
|
18
|
+
import '../apis/definitions/OAuthRequestApi.esm.js';
|
|
19
|
+
import '../apis/definitions/RouteResolutionApi.esm.js';
|
|
20
|
+
import '../apis/definitions/StorageApi.esm.js';
|
|
21
|
+
import '../apis/definitions/AnalyticsApi.esm.js';
|
|
22
|
+
import '../apis/definitions/TranslationApi.esm.js';
|
|
23
|
+
import '../apis/definitions/PluginHeaderActionsApi.esm.js';
|
|
24
|
+
import '@backstage/version-bridge';
|
|
25
|
+
import '@backstage/errors';
|
|
26
|
+
import '../components/AppNodeProvider.esm.js';
|
|
27
|
+
import '../components/DefaultSwappableComponents.esm.js';
|
|
28
|
+
import '../components/PageLayout.esm.js';
|
|
29
|
+
import { coreExtensionData } from '../wiring/coreExtensionData.esm.js';
|
|
30
|
+
import 'zod';
|
|
31
|
+
import 'zod-to-json-schema';
|
|
32
|
+
import { createExtensionBlueprint, createExtensionBlueprintParams } from '../wiring/createExtensionBlueprint.esm.js';
|
|
33
|
+
|
|
34
|
+
const PluginHeaderActionBlueprint = createExtensionBlueprint({
|
|
35
|
+
kind: "plugin-header-action",
|
|
36
|
+
attachTo: { id: "api:app/plugin-header-actions", input: "actions" },
|
|
37
|
+
output: [coreExtensionData.reactElement],
|
|
38
|
+
defineParams(params) {
|
|
39
|
+
return createExtensionBlueprintParams(params);
|
|
40
|
+
},
|
|
41
|
+
*factory(params, { node }) {
|
|
42
|
+
const LazyAction = lazy(
|
|
43
|
+
() => params.loader().then((element) => ({ default: () => element }))
|
|
44
|
+
);
|
|
45
|
+
yield coreExtensionData.reactElement(
|
|
46
|
+
/* @__PURE__ */ jsx(ExtensionBoundary, { node, errorPresentation: "error-api", children: /* @__PURE__ */ jsx(LazyAction, {}) })
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export { PluginHeaderActionBlueprint };
|
|
52
|
+
//# sourceMappingURL=PluginHeaderActionBlueprint.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginHeaderActionBlueprint.esm.js","sources":["../../src/blueprints/PluginHeaderActionBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2025 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 { lazy as reactLazy } from 'react';\nimport { ExtensionBoundary } from '../components';\nimport {\n coreExtensionData,\n createExtensionBlueprint,\n createExtensionBlueprintParams,\n} from '../wiring';\n\n/**\n * Creates extensions that provide plugin-scoped header actions.\n *\n * @remarks\n *\n * These actions are automatically scoped to the plugin that provides them\n * and will appear in the header of all pages belonging to that plugin.\n *\n * @public\n */\nexport const PluginHeaderActionBlueprint = createExtensionBlueprint({\n kind: 'plugin-header-action',\n attachTo: { id: 'api:app/plugin-header-actions', input: 'actions' },\n output: [coreExtensionData.reactElement],\n defineParams(params: { loader: () => Promise<JSX.Element> }) {\n return createExtensionBlueprintParams(params);\n },\n *factory(params, { node }) {\n const LazyAction = reactLazy(() =>\n params.loader().then(element => ({ default: () => element })),\n );\n yield coreExtensionData.reactElement(\n <ExtensionBoundary node={node} errorPresentation=\"error-api\">\n <LazyAction />\n </ExtensionBoundary>,\n );\n },\n});\n"],"names":["reactLazy"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCO,MAAM,8BAA8B,wBAAA,CAAyB;AAAA,EAClE,IAAA,EAAM,sBAAA;AAAA,EACN,QAAA,EAAU,EAAE,EAAA,EAAI,+BAAA,EAAiC,OAAO,SAAA,EAAU;AAAA,EAClE,MAAA,EAAQ,CAAC,iBAAA,CAAkB,YAAY,CAAA;AAAA,EACvC,aAAa,MAAA,EAAgD;AAC3D,IAAA,OAAO,+BAA+B,MAAM,CAAA;AAAA,EAC9C,CAAA;AAAA,EACA,CAAC,OAAA,CAAQ,MAAA,EAAQ,EAAE,MAAK,EAAG;AACzB,IAAA,MAAM,UAAA,GAAaA,IAAA;AAAA,MAAU,MAC3B,MAAA,CAAO,MAAA,EAAO,CAAE,IAAA,CAAK,cAAY,EAAE,OAAA,EAAS,MAAM,OAAA,EAAQ,CAAE;AAAA,KAC9D;AACA,IAAA,MAAM,iBAAA,CAAkB,YAAA;AAAA,0BACrB,iBAAA,EAAA,EAAkB,IAAA,EAAY,mBAAkB,WAAA,EAC/C,QAAA,kBAAA,GAAA,CAAC,cAAW,CAAA,EACd;AAAA,KACF;AAAA,EACF;AACF,CAAC;;;;"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { coreExtensionData } from '../wiring/coreExtensionData.esm.js';
|
|
2
|
+
import 'zod';
|
|
3
|
+
import 'zod-to-json-schema';
|
|
4
|
+
import { createExtensionBlueprint } from '../wiring/createExtensionBlueprint.esm.js';
|
|
5
|
+
import { ExtensionBoundary } from '../components/ExtensionBoundary.esm.js';
|
|
6
|
+
import 'react/jsx-runtime';
|
|
7
|
+
import '../apis/definitions/AppTreeApi.esm.js';
|
|
8
|
+
import '../apis/definitions/auth.esm.js';
|
|
9
|
+
import '../apis/definitions/AlertApi.esm.js';
|
|
10
|
+
import '../apis/definitions/AppLanguageApi.esm.js';
|
|
11
|
+
import '../apis/definitions/AppThemeApi.esm.js';
|
|
12
|
+
import '../apis/definitions/SwappableComponentsApi.esm.js';
|
|
13
|
+
import '../apis/definitions/ConfigApi.esm.js';
|
|
14
|
+
import '../apis/definitions/DiscoveryApi.esm.js';
|
|
15
|
+
import '../apis/definitions/ErrorApi.esm.js';
|
|
16
|
+
import '../apis/definitions/FeatureFlagsApi.esm.js';
|
|
17
|
+
import '../apis/definitions/FetchApi.esm.js';
|
|
18
|
+
import '../apis/definitions/IconsApi.esm.js';
|
|
19
|
+
import '../apis/definitions/IdentityApi.esm.js';
|
|
20
|
+
import '../apis/definitions/DialogApi.esm.js';
|
|
21
|
+
import '../apis/definitions/OAuthRequestApi.esm.js';
|
|
22
|
+
import '../apis/definitions/RouteResolutionApi.esm.js';
|
|
23
|
+
import '../apis/definitions/StorageApi.esm.js';
|
|
24
|
+
import '../apis/definitions/AnalyticsApi.esm.js';
|
|
25
|
+
import '../apis/definitions/TranslationApi.esm.js';
|
|
26
|
+
import '../apis/definitions/PluginHeaderActionsApi.esm.js';
|
|
27
|
+
import '@backstage/version-bridge';
|
|
28
|
+
import '@backstage/errors';
|
|
29
|
+
import 'react';
|
|
30
|
+
import '../components/AppNodeProvider.esm.js';
|
|
31
|
+
import '../components/DefaultSwappableComponents.esm.js';
|
|
32
|
+
import '../components/PageLayout.esm.js';
|
|
33
|
+
|
|
34
|
+
const SubPageBlueprint = createExtensionBlueprint({
|
|
35
|
+
kind: "sub-page",
|
|
36
|
+
attachTo: { relative: { kind: "page" }, input: "pages" },
|
|
37
|
+
output: [
|
|
38
|
+
coreExtensionData.routePath,
|
|
39
|
+
coreExtensionData.reactElement,
|
|
40
|
+
coreExtensionData.title,
|
|
41
|
+
coreExtensionData.routeRef.optional(),
|
|
42
|
+
coreExtensionData.icon.optional()
|
|
43
|
+
],
|
|
44
|
+
config: {
|
|
45
|
+
schema: {
|
|
46
|
+
path: (z) => z.string().optional(),
|
|
47
|
+
title: (z) => z.string().optional()
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
*factory(params, { config, node }) {
|
|
51
|
+
yield coreExtensionData.routePath(config.path ?? params.path);
|
|
52
|
+
yield coreExtensionData.title(config.title ?? params.title);
|
|
53
|
+
yield coreExtensionData.reactElement(
|
|
54
|
+
ExtensionBoundary.lazy(node, params.loader)
|
|
55
|
+
);
|
|
56
|
+
if (params.routeRef) {
|
|
57
|
+
yield coreExtensionData.routeRef(params.routeRef);
|
|
58
|
+
}
|
|
59
|
+
if (params.icon) {
|
|
60
|
+
yield coreExtensionData.icon(params.icon);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export { SubPageBlueprint };
|
|
66
|
+
//# sourceMappingURL=SubPageBlueprint.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubPageBlueprint.esm.js","sources":["../../src/blueprints/SubPageBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2026 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 { IconElement } from '../icons/types';\nimport { RouteRef } from '../routing';\nimport { coreExtensionData, createExtensionBlueprint } from '../wiring';\nimport { ExtensionBoundary } from '../components';\n\n/**\n * Creates extensions that are sub-page React components attached to a parent page.\n * Sub-pages are rendered as tabs within the parent page's header.\n *\n * @public\n * @example\n * ```tsx\n * const overviewRouteRef = createRouteRef();\n *\n * const mySubPage = SubPageBlueprint.make({\n * attachTo: { id: 'page:my-plugin', input: 'pages' },\n * name: 'overview',\n * params: {\n * path: 'overview',\n * title: 'Overview',\n * routeRef: overviewRouteRef,\n * loader: () => import('./components/Overview').then(m => <m.Overview />),\n * },\n * });\n * ```\n */\nexport const SubPageBlueprint = createExtensionBlueprint({\n kind: 'sub-page',\n attachTo: { relative: { kind: 'page' }, input: 'pages' },\n output: [\n coreExtensionData.routePath,\n coreExtensionData.reactElement,\n coreExtensionData.title,\n coreExtensionData.routeRef.optional(),\n coreExtensionData.icon.optional(),\n ],\n config: {\n schema: {\n path: z => z.string().optional(),\n title: z => z.string().optional(),\n },\n },\n *factory(\n params: {\n /**\n * The path for this sub-page, relative to the parent page. Must **not** start with '/'.\n *\n * @example 'overview', 'settings', 'details'\n */\n path: string;\n /**\n * The title displayed in the tab for this sub-page.\n */\n title: string;\n /**\n * Optional icon for this sub-page, displayed in the tab.\n */\n icon?: IconElement;\n /**\n * A function that returns a promise resolving to the React element to render.\n * This enables lazy loading of the sub-page content.\n */\n loader: () => Promise<JSX.Element>;\n /**\n * Optional route reference for this sub-page.\n */\n routeRef?: RouteRef;\n },\n { config, node },\n ) {\n yield coreExtensionData.routePath(config.path ?? params.path);\n yield coreExtensionData.title(config.title ?? params.title);\n yield coreExtensionData.reactElement(\n ExtensionBoundary.lazy(node, params.loader),\n );\n if (params.routeRef) {\n yield coreExtensionData.routeRef(params.routeRef);\n }\n if (params.icon) {\n yield coreExtensionData.icon(params.icon);\n }\n },\n});\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CO,MAAM,mBAAmB,wBAAA,CAAyB;AAAA,EACvD,IAAA,EAAM,UAAA;AAAA,EACN,QAAA,EAAU,EAAE,QAAA,EAAU,EAAE,MAAM,MAAA,EAAO,EAAG,OAAO,OAAA,EAAQ;AAAA,EACvD,MAAA,EAAQ;AAAA,IACN,iBAAA,CAAkB,SAAA;AAAA,IAClB,iBAAA,CAAkB,YAAA;AAAA,IAClB,iBAAA,CAAkB,KAAA;AAAA,IAClB,iBAAA,CAAkB,SAAS,QAAA,EAAS;AAAA,IACpC,iBAAA,CAAkB,KAAK,QAAA;AAAS,GAClC;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS;AAAA,MAC/B,KAAA,EAAO,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,GAAS,QAAA;AAAS;AAClC,GACF;AAAA,EACA,CAAC,OAAA,CACC,MAAA,EAyBA,EAAE,MAAA,EAAQ,MAAK,EACf;AACA,IAAA,MAAM,iBAAA,CAAkB,SAAA,CAAU,MAAA,CAAO,IAAA,IAAQ,OAAO,IAAI,CAAA;AAC5D,IAAA,MAAM,iBAAA,CAAkB,KAAA,CAAM,MAAA,CAAO,KAAA,IAAS,OAAO,KAAK,CAAA;AAC1D,IAAA,MAAM,iBAAA,CAAkB,YAAA;AAAA,MACtB,iBAAA,CAAkB,IAAA,CAAK,IAAA,EAAM,MAAA,CAAO,MAAM;AAAA,KAC5C;AACA,IAAA,IAAI,OAAO,QAAA,EAAU;AACnB,MAAA,MAAM,iBAAA,CAAkB,QAAA,CAAS,MAAA,CAAO,QAAQ,CAAA;AAAA,IAClD;AACA,IAAA,IAAI,OAAO,IAAA,EAAM;AACf,MAAA,MAAM,iBAAA,CAAkB,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA;AAAA,IAC1C;AAAA,EACF;AACF,CAAC;;;;"}
|
|
@@ -24,6 +24,7 @@ import '../apis/definitions/RouteResolutionApi.esm.js';
|
|
|
24
24
|
import '../apis/definitions/StorageApi.esm.js';
|
|
25
25
|
import '../apis/definitions/AnalyticsApi.esm.js';
|
|
26
26
|
import '../apis/definitions/TranslationApi.esm.js';
|
|
27
|
+
import '../apis/definitions/PluginHeaderActionsApi.esm.js';
|
|
27
28
|
import { useApi } from '../apis/system/useApi.esm.js';
|
|
28
29
|
import { pluginWrapperApiRef } from '../apis/definitions/PluginWrapperApi.esm.js';
|
|
29
30
|
import { coreExtensionData } from '../wiring/coreExtensionData.esm.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExtensionBoundary.esm.js","sources":["../../src/components/ExtensionBoundary.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 {\n PropsWithChildren,\n ReactNode,\n Suspense,\n useEffect,\n useMemo,\n lazy as reactLazy,\n} from 'react';\nimport { AnalyticsContext, useAnalytics } from '../analytics';\nimport { ErrorDisplayBoundary } from './ErrorDisplayBoundary';\nimport { ErrorApiBoundary } from './ErrorApiBoundary';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker';\nimport { AppNode, ErrorApi, errorApiRef, useApi } from '../apis';\nimport {\n PluginWrapperApi,\n pluginWrapperApiRef,\n} from '../apis/definitions/PluginWrapperApi';\nimport { coreExtensionData } from '../wiring';\nimport { AppNodeProvider } from './AppNodeProvider';\nimport { Progress } from './DefaultSwappableComponents';\n\nfunction useOptionalErrorApi(): ErrorApi | undefined {\n try {\n return useApi(errorApiRef);\n } catch {\n return undefined;\n }\n}\n\nfunction useOptionalPluginWrapperApi(): PluginWrapperApi | undefined {\n try {\n return useApi(pluginWrapperApiRef);\n } catch {\n return undefined;\n }\n}\ntype RouteTrackerProps = PropsWithChildren<{\n enabled?: boolean;\n}>;\n\nconst RouteTracker = (props: RouteTrackerProps) => {\n const { enabled, children } = props;\n const analytics = useAnalytics();\n\n // This event, never exposed to end-users of the analytics API,\n // helps inform which extension metadata gets associated with a\n // navigation event when the route navigated to is a gathered\n // mountpoint.\n useEffect(() => {\n if (enabled) {\n analytics.captureEvent(routableExtensionRenderedEvent, '');\n }\n }, [analytics, enabled]);\n\n return <>{children}</>;\n};\n\n/** @public */\nexport interface ExtensionBoundaryProps {\n errorPresentation?: 'error-api' | 'error-display';\n node: AppNode;\n children: ReactNode;\n}\n\n/** @public */\nexport function ExtensionBoundary(props: ExtensionBoundaryProps) {\n const { node, children } = props;\n\n const errorApi = useOptionalErrorApi();\n\n const hasRoutePathOutput = Boolean(\n node.instance?.getData(coreExtensionData.routePath),\n );\n\n const plugin = node.spec.plugin;\n const pluginId = plugin.pluginId ?? 'app';\n\n const pluginWrapperApi = useOptionalPluginWrapperApi();\n\n const PluginWrapper = useMemo(() => {\n return pluginWrapperApi?.getPluginWrapper(pluginId);\n }, [pluginWrapperApi, pluginId]);\n\n // Skipping \"routeRef\" attribute in the new system, the extension \"id\" should provide more insight\n const attributes = {\n extensionId: node.spec.id,\n pluginId,\n };\n\n let content = (\n <AnalyticsContext attributes={attributes}>\n <RouteTracker enabled={hasRoutePathOutput}>{children}</RouteTracker>\n </AnalyticsContext>\n );\n\n if (PluginWrapper) {\n content = <PluginWrapper>{content}</PluginWrapper>;\n }\n\n if (props.errorPresentation === 'error-api') {\n content = (\n <ErrorApiBoundary node={node} errorApi={errorApi}>\n {content}\n </ErrorApiBoundary>\n );\n } else {\n content = (\n <ErrorDisplayBoundary plugin={plugin}>{content}</ErrorDisplayBoundary>\n );\n }\n\n return (\n <AppNodeProvider node={node}>\n <Suspense fallback={<Progress />}>{content}</Suspense>\n </AppNodeProvider>\n );\n}\n\n/** @public */\nexport namespace ExtensionBoundary {\n export function lazy(\n appNode: AppNode,\n loader: () => Promise<JSX.Element>,\n ): JSX.Element {\n const ExtensionComponent = reactLazy(() =>\n loader().then(element => ({ default: () => element })),\n );\n return (\n <ExtensionBoundary node={appNode}>\n <ExtensionComponent />\n </ExtensionBoundary>\n );\n }\n\n export function lazyComponent<TProps extends {}>(\n appNode: AppNode,\n loader: () => Promise<(props: TProps) => JSX.Element>,\n ): (props: TProps) => JSX.Element {\n const ExtensionComponent = reactLazy(() =>\n loader().then(Component => ({ default: Component })),\n ) as unknown as React.ComponentType<TProps>;\n\n return (props: TProps) => (\n <ExtensionBoundary node={appNode}>\n <ExtensionComponent {...props} />\n </ExtensionBoundary>\n );\n }\n}\n"],"names":["ExtensionBoundary","lazy","reactLazy"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExtensionBoundary.esm.js","sources":["../../src/components/ExtensionBoundary.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 {\n PropsWithChildren,\n ReactNode,\n Suspense,\n useEffect,\n useMemo,\n lazy as reactLazy,\n} from 'react';\nimport { AnalyticsContext, useAnalytics } from '../analytics';\nimport { ErrorDisplayBoundary } from './ErrorDisplayBoundary';\nimport { ErrorApiBoundary } from './ErrorApiBoundary';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker';\nimport { AppNode, ErrorApi, errorApiRef, useApi } from '../apis';\nimport {\n PluginWrapperApi,\n pluginWrapperApiRef,\n} from '../apis/definitions/PluginWrapperApi';\nimport { coreExtensionData } from '../wiring';\nimport { AppNodeProvider } from './AppNodeProvider';\nimport { Progress } from './DefaultSwappableComponents';\n\nfunction useOptionalErrorApi(): ErrorApi | undefined {\n try {\n return useApi(errorApiRef);\n } catch {\n return undefined;\n }\n}\n\nfunction useOptionalPluginWrapperApi(): PluginWrapperApi | undefined {\n try {\n return useApi(pluginWrapperApiRef);\n } catch {\n return undefined;\n }\n}\ntype RouteTrackerProps = PropsWithChildren<{\n enabled?: boolean;\n}>;\n\nconst RouteTracker = (props: RouteTrackerProps) => {\n const { enabled, children } = props;\n const analytics = useAnalytics();\n\n // This event, never exposed to end-users of the analytics API,\n // helps inform which extension metadata gets associated with a\n // navigation event when the route navigated to is a gathered\n // mountpoint.\n useEffect(() => {\n if (enabled) {\n analytics.captureEvent(routableExtensionRenderedEvent, '');\n }\n }, [analytics, enabled]);\n\n return <>{children}</>;\n};\n\n/** @public */\nexport interface ExtensionBoundaryProps {\n errorPresentation?: 'error-api' | 'error-display';\n node: AppNode;\n children: ReactNode;\n}\n\n/** @public */\nexport function ExtensionBoundary(props: ExtensionBoundaryProps) {\n const { node, children } = props;\n\n const errorApi = useOptionalErrorApi();\n\n const hasRoutePathOutput = Boolean(\n node.instance?.getData(coreExtensionData.routePath),\n );\n\n const plugin = node.spec.plugin;\n const pluginId = plugin.pluginId ?? 'app';\n\n const pluginWrapperApi = useOptionalPluginWrapperApi();\n\n const PluginWrapper = useMemo(() => {\n return pluginWrapperApi?.getPluginWrapper(pluginId);\n }, [pluginWrapperApi, pluginId]);\n\n // Skipping \"routeRef\" attribute in the new system, the extension \"id\" should provide more insight\n const attributes = {\n extensionId: node.spec.id,\n pluginId,\n };\n\n let content = (\n <AnalyticsContext attributes={attributes}>\n <RouteTracker enabled={hasRoutePathOutput}>{children}</RouteTracker>\n </AnalyticsContext>\n );\n\n if (PluginWrapper) {\n content = <PluginWrapper>{content}</PluginWrapper>;\n }\n\n if (props.errorPresentation === 'error-api') {\n content = (\n <ErrorApiBoundary node={node} errorApi={errorApi}>\n {content}\n </ErrorApiBoundary>\n );\n } else {\n content = (\n <ErrorDisplayBoundary plugin={plugin}>{content}</ErrorDisplayBoundary>\n );\n }\n\n return (\n <AppNodeProvider node={node}>\n <Suspense fallback={<Progress />}>{content}</Suspense>\n </AppNodeProvider>\n );\n}\n\n/** @public */\nexport namespace ExtensionBoundary {\n export function lazy(\n appNode: AppNode,\n loader: () => Promise<JSX.Element>,\n ): JSX.Element {\n const ExtensionComponent = reactLazy(() =>\n loader().then(element => ({ default: () => element })),\n );\n return (\n <ExtensionBoundary node={appNode}>\n <ExtensionComponent />\n </ExtensionBoundary>\n );\n }\n\n export function lazyComponent<TProps extends {}>(\n appNode: AppNode,\n loader: () => Promise<(props: TProps) => JSX.Element>,\n ): (props: TProps) => JSX.Element {\n const ExtensionComponent = reactLazy(() =>\n loader().then(Component => ({ default: Component })),\n ) as unknown as React.ComponentType<TProps>;\n\n return (props: TProps) => (\n <ExtensionBoundary node={appNode}>\n <ExtensionComponent {...props} />\n </ExtensionBoundary>\n );\n }\n}\n"],"names":["ExtensionBoundary","lazy","reactLazy"],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { createSwappableComponent } from './createSwappableComponent.esm.js';
|
|
3
|
+
|
|
4
|
+
function DefaultPageLayout(props) {
|
|
5
|
+
const { title, icon, headerActions, tabs, children } = props;
|
|
6
|
+
return /* @__PURE__ */ jsxs(
|
|
7
|
+
"div",
|
|
8
|
+
{
|
|
9
|
+
"data-component": "page-layout",
|
|
10
|
+
style: {
|
|
11
|
+
display: "flex",
|
|
12
|
+
flexDirection: "column",
|
|
13
|
+
flexGrow: 1,
|
|
14
|
+
minHeight: 0
|
|
15
|
+
},
|
|
16
|
+
children: [
|
|
17
|
+
(title || tabs) && /* @__PURE__ */ jsxs(
|
|
18
|
+
"header",
|
|
19
|
+
{
|
|
20
|
+
style: {
|
|
21
|
+
borderBottom: "1px solid #ddd",
|
|
22
|
+
backgroundColor: "#fff",
|
|
23
|
+
flexShrink: 0
|
|
24
|
+
},
|
|
25
|
+
children: [
|
|
26
|
+
title && /* @__PURE__ */ jsxs(
|
|
27
|
+
"div",
|
|
28
|
+
{
|
|
29
|
+
style: {
|
|
30
|
+
display: "flex",
|
|
31
|
+
alignItems: "center",
|
|
32
|
+
gap: "8px",
|
|
33
|
+
padding: "12px 24px 8px",
|
|
34
|
+
fontSize: "18px",
|
|
35
|
+
fontWeight: 500
|
|
36
|
+
},
|
|
37
|
+
children: [
|
|
38
|
+
icon,
|
|
39
|
+
title,
|
|
40
|
+
headerActions && /* @__PURE__ */ jsx("div", { style: { marginLeft: "auto" }, children: headerActions })
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
tabs && tabs.length > 0 && /* @__PURE__ */ jsx(
|
|
45
|
+
"nav",
|
|
46
|
+
{
|
|
47
|
+
style: {
|
|
48
|
+
display: "flex",
|
|
49
|
+
gap: "4px",
|
|
50
|
+
padding: "0 24px"
|
|
51
|
+
},
|
|
52
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxs(
|
|
53
|
+
"a",
|
|
54
|
+
{
|
|
55
|
+
href: tab.href,
|
|
56
|
+
style: {
|
|
57
|
+
display: "flex",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
gap: "4px",
|
|
60
|
+
padding: "8px 12px",
|
|
61
|
+
textDecoration: "none",
|
|
62
|
+
color: "#333",
|
|
63
|
+
borderBottom: "2px solid transparent"
|
|
64
|
+
},
|
|
65
|
+
children: [
|
|
66
|
+
tab.icon,
|
|
67
|
+
tab.label
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
tab.id
|
|
71
|
+
))
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
/* @__PURE__ */ jsx(
|
|
78
|
+
"div",
|
|
79
|
+
{
|
|
80
|
+
style: {
|
|
81
|
+
display: "flex",
|
|
82
|
+
flexDirection: "column",
|
|
83
|
+
flexGrow: 1,
|
|
84
|
+
minHeight: 0
|
|
85
|
+
},
|
|
86
|
+
children
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
const PageLayout = createSwappableComponent({
|
|
94
|
+
id: "core.page-layout",
|
|
95
|
+
loader: () => DefaultPageLayout
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export { PageLayout };
|
|
99
|
+
//# sourceMappingURL=PageLayout.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageLayout.esm.js","sources":["../../src/components/PageLayout.tsx"],"sourcesContent":["/*\n * Copyright 2024 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 { ReactNode } from 'react';\nimport { IconElement } from '../icons/types';\nimport { createSwappableComponent } from './createSwappableComponent';\n\n/**\n * Tab configuration for page navigation\n * @public\n */\nexport interface PageTab {\n id: string;\n label: string;\n icon?: IconElement;\n href: string;\n}\n\n/**\n * Props for the PageLayout component\n * @public\n */\nexport interface PageLayoutProps {\n title?: string;\n icon?: IconElement;\n noHeader?: boolean;\n headerActions?: Array<JSX.Element | null>;\n tabs?: PageTab[];\n children?: ReactNode;\n}\n\n/**\n * Default implementation of PageLayout using plain HTML elements\n */\nfunction DefaultPageLayout(props: PageLayoutProps): JSX.Element {\n const { title, icon, headerActions, tabs, children } = props;\n\n return (\n <div\n data-component=\"page-layout\"\n style={{\n display: 'flex',\n flexDirection: 'column',\n flexGrow: 1,\n minHeight: 0,\n }}\n >\n {(title || tabs) && (\n <header\n style={{\n borderBottom: '1px solid #ddd',\n backgroundColor: '#fff',\n flexShrink: 0,\n }}\n >\n {title && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n padding: '12px 24px 8px',\n fontSize: '18px',\n fontWeight: 500,\n }}\n >\n {icon}\n {title}\n {headerActions && (\n <div style={{ marginLeft: 'auto' }}>{headerActions}</div>\n )}\n </div>\n )}\n {tabs && tabs.length > 0 && (\n <nav\n style={{\n display: 'flex',\n gap: '4px',\n padding: '0 24px',\n }}\n >\n {tabs.map(tab => (\n <a\n key={tab.id}\n href={tab.href}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n padding: '8px 12px',\n textDecoration: 'none',\n color: '#333',\n borderBottom: '2px solid transparent',\n }}\n >\n {tab.icon}\n {tab.label}\n </a>\n ))}\n </nav>\n )}\n </header>\n )}\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n flexGrow: 1,\n minHeight: 0,\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n\n/**\n * Swappable component for laying out page content with header and navigation.\n * The default implementation uses plain HTML elements.\n * Apps can override this with a custom implementation (e.g., using \\@backstage/ui).\n *\n * @public\n */\nexport const PageLayout = createSwappableComponent<PageLayoutProps>({\n id: 'core.page-layout',\n loader: () => DefaultPageLayout,\n});\n"],"names":[],"mappings":";;;AA+CA,SAAS,kBAAkB,KAAA,EAAqC;AAC9D,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,UAAS,GAAI,KAAA;AAEvD,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,gBAAA,EAAe,aAAA;AAAA,MACf,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,MAAA;AAAA,QACT,aAAA,EAAe,QAAA;AAAA,QACf,QAAA,EAAU,CAAA;AAAA,QACV,SAAA,EAAW;AAAA,OACb;AAAA,MAEE,QAAA,EAAA;AAAA,QAAA,CAAA,KAAA,IAAS,IAAA,qBACT,IAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO;AAAA,cACL,YAAA,EAAc,gBAAA;AAAA,cACd,eAAA,EAAiB,MAAA;AAAA,cACjB,UAAA,EAAY;AAAA,aACd;AAAA,YAEC,QAAA,EAAA;AAAA,cAAA,KAAA,oBACC,IAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO;AAAA,oBACL,OAAA,EAAS,MAAA;AAAA,oBACT,UAAA,EAAY,QAAA;AAAA,oBACZ,GAAA,EAAK,KAAA;AAAA,oBACL,OAAA,EAAS,eAAA;AAAA,oBACT,QAAA,EAAU,MAAA;AAAA,oBACV,UAAA,EAAY;AAAA,mBACd;AAAA,kBAEC,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,oBACA,KAAA;AAAA,oBACA,aAAA,wBACE,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,UAAA,EAAY,MAAA,IAAW,QAAA,EAAA,aAAA,EAAc;AAAA;AAAA;AAAA,eAEvD;AAAA,cAED,IAAA,IAAQ,IAAA,CAAK,MAAA,GAAS,CAAA,oBACrB,GAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO;AAAA,oBACL,OAAA,EAAS,MAAA;AAAA,oBACT,GAAA,EAAK,KAAA;AAAA,oBACL,OAAA,EAAS;AAAA,mBACX;AAAA,kBAEC,QAAA,EAAA,IAAA,CAAK,IAAI,CAAA,GAAA,qBACR,IAAA;AAAA,oBAAC,GAAA;AAAA,oBAAA;AAAA,sBAEC,MAAM,GAAA,CAAI,IAAA;AAAA,sBACV,KAAA,EAAO;AAAA,wBACL,OAAA,EAAS,MAAA;AAAA,wBACT,UAAA,EAAY,QAAA;AAAA,wBACZ,GAAA,EAAK,KAAA;AAAA,wBACL,OAAA,EAAS,UAAA;AAAA,wBACT,cAAA,EAAgB,MAAA;AAAA,wBAChB,KAAA,EAAO,MAAA;AAAA,wBACP,YAAA,EAAc;AAAA,uBAChB;AAAA,sBAEC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAI,IAAA;AAAA,wBACJ,GAAA,CAAI;AAAA;AAAA,qBAAA;AAAA,oBAbA,GAAA,CAAI;AAAA,mBAeZ;AAAA;AAAA;AACH;AAAA;AAAA,SAEJ;AAAA,wBAEF,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO;AAAA,cACL,OAAA,EAAS,MAAA;AAAA,cACT,aAAA,EAAe,QAAA;AAAA,cACf,QAAA,EAAU,CAAA;AAAA,cACV,SAAA,EAAW;AAAA,aACb;AAAA,YAEC;AAAA;AAAA;AACH;AAAA;AAAA,GACF;AAEJ;AASO,MAAM,aAAa,wBAAA,CAA0C;AAAA,EAClE,EAAA,EAAI,kBAAA;AAAA,EACJ,QAAQ,MAAM;AAChB,CAAC;;;;"}
|
|
@@ -18,6 +18,7 @@ import '../apis/definitions/RouteResolutionApi.esm.js';
|
|
|
18
18
|
import '../apis/definitions/StorageApi.esm.js';
|
|
19
19
|
import '../apis/definitions/AnalyticsApi.esm.js';
|
|
20
20
|
import '../apis/definitions/TranslationApi.esm.js';
|
|
21
|
+
import '../apis/definitions/PluginHeaderActionsApi.esm.js';
|
|
21
22
|
import { useApi } from '../apis/system/useApi.esm.js';
|
|
22
23
|
import { lazy } from 'react';
|
|
23
24
|
import { OpaqueSwappableComponentRef } from '../frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSwappableComponent.esm.js","sources":["../../src/components/createSwappableComponent.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 { OpaqueSwappableComponentRef } from '@internal/frontend';\nimport { swappableComponentsApiRef, useApi } from '../apis';\nimport { lazy } from 'react';\n\n/** @public */\nexport type SwappableComponentRef<\n TInnerComponentProps extends {} = {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n> = {\n id: string;\n TProps: TInnerComponentProps;\n TExternalProps: TExternalComponentProps;\n $$type: '@backstage/SwappableComponentRef';\n};\n\n/**\n * Options for creating an SwappableComponent.\n *\n * @public\n */\nexport type CreateSwappableComponentOptions<\n TInnerComponentProps extends {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n> = {\n id: string;\n loader?:\n | (() => (props: TInnerComponentProps) => JSX.Element | null)\n | (() => Promise<(props: TInnerComponentProps) => JSX.Element | null>);\n transformProps?: (props: TExternalComponentProps) => TInnerComponentProps;\n};\n\nconst useComponentRefApi = () => {\n try {\n return useApi(swappableComponentsApiRef);\n } catch (e) {\n return undefined;\n }\n};\n\n/**\n * Creates a SwappableComponent that can be used to render the component, optionally overridden by the app.\n *\n * @public\n */\nexport function createSwappableComponent<\n TInnerComponentProps extends {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n>(\n options: CreateSwappableComponentOptions<\n TInnerComponentProps,\n TExternalComponentProps\n >,\n): {\n (props: TExternalComponentProps): JSX.Element | null;\n ref: SwappableComponentRef<TInnerComponentProps, TExternalComponentProps>;\n} {\n const FallbackComponent = (p: JSX.IntrinsicAttributes) => (\n <div data-testid={options.id} {...p} />\n );\n\n const ref = OpaqueSwappableComponentRef.createInstance('v1', {\n id: options.id,\n TProps: null as unknown as TInnerComponentProps,\n TExternalProps: null as unknown as TExternalComponentProps,\n toString() {\n return `SwappableComponentRef{id=${options.id}}`;\n },\n defaultComponent: lazy(async () => {\n const Component = (await options.loader?.()) ?? FallbackComponent;\n return { default: Component };\n }) as (typeof OpaqueSwappableComponentRef.TInternal)['defaultComponent'],\n transformProps:\n options.transformProps as (typeof OpaqueSwappableComponentRef.TInternal)['transformProps'],\n });\n\n const ComponentRefImpl = (props: TExternalComponentProps) => {\n const api = useComponentRefApi();\n\n if (!api) {\n const internalRef = OpaqueSwappableComponentRef.toInternal(ref);\n const Component = internalRef.defaultComponent;\n const innerProps = internalRef.transformProps?.(props) ?? props;\n return <Component {...innerProps} />;\n }\n\n const Component = api.getComponent<any>(ref);\n return <Component {...props} />;\n };\n\n Object.assign(ComponentRefImpl, { ref });\n\n return ComponentRefImpl as {\n (props: TExternalComponentProps): JSX.Element | null;\n ref: SwappableComponentRef<TInnerComponentProps, TExternalComponentProps>;\n };\n}\n"],"names":["Component"],"mappings":"
|
|
1
|
+
{"version":3,"file":"createSwappableComponent.esm.js","sources":["../../src/components/createSwappableComponent.tsx"],"sourcesContent":["/*\n * Copyright 2023 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 { OpaqueSwappableComponentRef } from '@internal/frontend';\nimport { swappableComponentsApiRef, useApi } from '../apis';\nimport { lazy } from 'react';\n\n/** @public */\nexport type SwappableComponentRef<\n TInnerComponentProps extends {} = {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n> = {\n id: string;\n TProps: TInnerComponentProps;\n TExternalProps: TExternalComponentProps;\n $$type: '@backstage/SwappableComponentRef';\n};\n\n/**\n * Options for creating an SwappableComponent.\n *\n * @public\n */\nexport type CreateSwappableComponentOptions<\n TInnerComponentProps extends {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n> = {\n id: string;\n loader?:\n | (() => (props: TInnerComponentProps) => JSX.Element | null)\n | (() => Promise<(props: TInnerComponentProps) => JSX.Element | null>);\n transformProps?: (props: TExternalComponentProps) => TInnerComponentProps;\n};\n\nconst useComponentRefApi = () => {\n try {\n return useApi(swappableComponentsApiRef);\n } catch (e) {\n return undefined;\n }\n};\n\n/**\n * Creates a SwappableComponent that can be used to render the component, optionally overridden by the app.\n *\n * @public\n */\nexport function createSwappableComponent<\n TInnerComponentProps extends {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n>(\n options: CreateSwappableComponentOptions<\n TInnerComponentProps,\n TExternalComponentProps\n >,\n): {\n (props: TExternalComponentProps): JSX.Element | null;\n ref: SwappableComponentRef<TInnerComponentProps, TExternalComponentProps>;\n} {\n const FallbackComponent = (p: JSX.IntrinsicAttributes) => (\n <div data-testid={options.id} {...p} />\n );\n\n const ref = OpaqueSwappableComponentRef.createInstance('v1', {\n id: options.id,\n TProps: null as unknown as TInnerComponentProps,\n TExternalProps: null as unknown as TExternalComponentProps,\n toString() {\n return `SwappableComponentRef{id=${options.id}}`;\n },\n defaultComponent: lazy(async () => {\n const Component = (await options.loader?.()) ?? FallbackComponent;\n return { default: Component };\n }) as (typeof OpaqueSwappableComponentRef.TInternal)['defaultComponent'],\n transformProps:\n options.transformProps as (typeof OpaqueSwappableComponentRef.TInternal)['transformProps'],\n });\n\n const ComponentRefImpl = (props: TExternalComponentProps) => {\n const api = useComponentRefApi();\n\n if (!api) {\n const internalRef = OpaqueSwappableComponentRef.toInternal(ref);\n const Component = internalRef.defaultComponent;\n const innerProps = internalRef.transformProps?.(props) ?? props;\n return <Component {...innerProps} />;\n }\n\n const Component = api.getComponent<any>(ref);\n return <Component {...props} />;\n };\n\n Object.assign(ComponentRefImpl, { ref });\n\n return ComponentRefImpl as {\n (props: TExternalComponentProps): JSX.Element | null;\n ref: SwappableComponentRef<TInnerComponentProps, TExternalComponentProps>;\n };\n}\n"],"names":["Component"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAM,qBAAqB,MAAM;AAC/B,EAAA,IAAI;AACF,IAAA,OAAO,OAAO,yBAAyB,CAAA;AAAA,EACzC,SAAS,CAAA,EAAG;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAA;AAOO,SAAS,yBAId,OAAA,EAOA;AACA,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,qBACzB,GAAA,CAAC,SAAI,aAAA,EAAa,OAAA,CAAQ,EAAA,EAAK,GAAG,CAAA,EAAG,CAAA;AAGvC,EAAA,MAAM,GAAA,GAAM,2BAAA,CAA4B,cAAA,CAAe,IAAA,EAAM;AAAA,IAC3D,IAAI,OAAA,CAAQ,EAAA;AAAA,IACZ,MAAA,EAAQ,IAAA;AAAA,IACR,cAAA,EAAgB,IAAA;AAAA,IAChB,QAAA,GAAW;AACT,MAAA,OAAO,CAAA,yBAAA,EAA4B,QAAQ,EAAE,CAAA,CAAA,CAAA;AAAA,IAC/C,CAAA;AAAA,IACA,gBAAA,EAAkB,KAAK,YAAY;AACjC,MAAA,MAAM,SAAA,GAAa,MAAM,OAAA,CAAQ,MAAA,IAAS,IAAM,iBAAA;AAChD,MAAA,OAAO,EAAE,SAAS,SAAA,EAAU;AAAA,IAC9B,CAAC,CAAA;AAAA,IACD,gBACE,OAAA,CAAQ;AAAA,GACX,CAAA;AAED,EAAA,MAAM,gBAAA,GAAmB,CAAC,KAAA,KAAmC;AAC3D,IAAA,MAAM,MAAM,kBAAA,EAAmB;AAE/B,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,MAAM,WAAA,GAAc,2BAAA,CAA4B,UAAA,CAAW,GAAG,CAAA;AAC9D,MAAA,MAAMA,aAAY,WAAA,CAAY,gBAAA;AAC9B,MAAA,MAAM,UAAA,GAAa,WAAA,CAAY,cAAA,GAAiB,KAAK,CAAA,IAAK,KAAA;AAC1D,MAAA,uBAAO,GAAA,CAACA,UAAAA,EAAA,EAAW,GAAG,UAAA,EAAY,CAAA;AAAA,IACpC;AAEA,IAAA,MAAM,SAAA,GAAY,GAAA,CAAI,YAAA,CAAkB,GAAG,CAAA;AAC3C,IAAA,uBAAO,GAAA,CAAC,SAAA,EAAA,EAAW,GAAG,KAAA,EAAO,CAAA;AAAA,EAC/B,CAAA;AAEA,EAAA,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,EAAE,GAAA,EAAK,CAAA;AAEvC,EAAA,OAAO,gBAAA;AAIT;;;;"}
|