@backstage/frontend-plugin-api 0.17.2 → 0.17.3-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 +18 -0
- package/dist/blueprints/PageBlueprint.esm.js +16 -1
- package/dist/blueprints/PageBlueprint.esm.js.map +1 -1
- package/dist/breadcrumbs/useBreadcrumbEntries.esm.js +40 -0
- package/dist/breadcrumbs/useBreadcrumbEntries.esm.js.map +1 -0
- package/dist/index.d.ts +34 -2
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -1
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @backstage/frontend-plugin-api
|
|
2
2
|
|
|
3
|
+
## 0.17.3-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a5b2811: Adds `useBreadcrumbEntries` hook, `BreadcrumbEntry` component, and `BreadcrumbsRegistryProvider` for managing breadcrumb trails across the component tree for plugins using new frontend system. Completes wiring so that new frontend system plugin `Pages` and `SubPages` get automatic-population of `PluginHeader` breadcrumbs.
|
|
8
|
+
|
|
9
|
+
- The app plugin's `PageLayout` registers a root breadcrumb for each plugin page and passes the breadcrumb trail to `PluginHeader`
|
|
10
|
+
- `PageBlueprint` automatically wraps each sub-page route element with `BreadcrumbEntry`, so sub-pages contribute to the PluginHeader breadcrumb trail without extra wiring.
|
|
11
|
+
- Plugin authors who need breadcrumbs for internal routes within a sub-page can wrap their route content with `BreadcrumbEntry` manually.
|
|
12
|
+
- `plugin-scaffolder` internal routes have been wrapped as an example
|
|
13
|
+
|
|
14
|
+
## 0.17.3-next.0
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
- @backstage/filter-predicates@0.1.4-next.0
|
|
20
|
+
|
|
3
21
|
## 0.17.2
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -34,6 +34,7 @@ import 'react';
|
|
|
34
34
|
import '../components/AppNodeProvider.esm.js';
|
|
35
35
|
import '../components/DefaultSwappableComponents.esm.js';
|
|
36
36
|
import { PageLayout } from '../components/PageLayout.esm.js';
|
|
37
|
+
import { BreadcrumbEntry } from '../breadcrumbs/useBreadcrumbEntries.esm.js';
|
|
37
38
|
|
|
38
39
|
function resolveTitleLink(routeResolutionApi, routeRef) {
|
|
39
40
|
if (!routeRef) {
|
|
@@ -133,8 +134,22 @@ const PageBlueprint = createExtensionBlueprint({
|
|
|
133
134
|
),
|
|
134
135
|
inputs.pages.map((page, index) => {
|
|
135
136
|
const path = page.get(coreExtensionData.routePath);
|
|
137
|
+
const tabTitle = page.get(coreExtensionData.title);
|
|
136
138
|
const element = page.get(coreExtensionData.reactElement);
|
|
137
|
-
return /* @__PURE__ */ jsx(
|
|
139
|
+
return /* @__PURE__ */ jsx(
|
|
140
|
+
Route,
|
|
141
|
+
{
|
|
142
|
+
path: `${path}/*`,
|
|
143
|
+
element: /* @__PURE__ */ jsx(
|
|
144
|
+
BreadcrumbEntry,
|
|
145
|
+
{
|
|
146
|
+
entry: { label: tabTitle || path, href: path },
|
|
147
|
+
children: element
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
},
|
|
151
|
+
index
|
|
152
|
+
);
|
|
138
153
|
})
|
|
139
154
|
] })
|
|
140
155
|
}
|
|
@@ -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 { z } from 'zod/v4';\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, PageLayoutTab } from '../components';\nimport { useApi } from '../apis/system';\nimport { routeResolutionApiRef } from '../apis/definitions/RouteResolutionApi';\nimport { pluginHeaderActionsApiRef } from '../apis/definitions/PluginHeaderActionsApi';\nimport { RouteResolutionApi } from '../apis/definitions/RouteResolutionApi';\n\nfunction resolveTitleLink(\n routeResolutionApi: RouteResolutionApi,\n routeRef: RouteRef | undefined,\n): string | undefined {\n if (!routeRef) {\n return undefined;\n }\n try {\n return routeResolutionApi.resolve(routeRef)?.();\n } catch {\n // Route ref may require params not available in the current context\n return undefined;\n }\n}\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 configSchema: {\n path: z.string().optional(),\n title: z.string().optional(),\n },\n *factory(\n params: {\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 const resolvedTitle =\n title ?? node.spec.plugin.title ?? node.spec.plugin.pluginId;\n const resolvedIcon = icon ?? node.spec.plugin.icon;\n const titleRouteRef =\n (node.spec.plugin.routes as { root?: RouteRef }).root ?? params.routeRef;\n\n yield coreExtensionData.routePath(config.path ?? params.path);\n if (params.loader) {\n const loader = params.loader;\n const PageContent = () => {\n const routeResolutionApi = useApi(routeResolutionApiRef);\n const titleLink = resolveTitleLink(routeResolutionApi, titleRouteRef);\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n\n return (\n <PageLayout\n title={resolvedTitle}\n icon={resolvedIcon}\n noHeader={noHeader}\n titleLink={titleLink}\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: PageLayoutTab[] = 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 const routeResolutionApi = useApi(routeResolutionApiRef);\n const titleLink = resolveTitleLink(routeResolutionApi, titleRouteRef);\n\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n\n return (\n <PageLayout\n title={resolvedTitle}\n icon={resolvedIcon}\n tabs={tabs}\n titleLink={titleLink}\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 routeResolutionApi = useApi(routeResolutionApiRef);\n const titleLink = resolveTitleLink(routeResolutionApi, titleRouteRef);\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n return (\n <PageLayout\n title={resolvedTitle}\n icon={resolvedIcon}\n titleLink={titleLink}\n headerActions={headerActions}\n />\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":"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"}
|
|
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 { z } from 'zod/v4';\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, PageLayoutTab } from '../components';\nimport { useApi } from '../apis/system';\nimport { BreadcrumbEntry } from '../breadcrumbs';\nimport { routeResolutionApiRef } from '../apis/definitions/RouteResolutionApi';\nimport { pluginHeaderActionsApiRef } from '../apis/definitions/PluginHeaderActionsApi';\nimport { RouteResolutionApi } from '../apis/definitions/RouteResolutionApi';\n\nfunction resolveTitleLink(\n routeResolutionApi: RouteResolutionApi,\n routeRef: RouteRef | undefined,\n): string | undefined {\n if (!routeRef) {\n return undefined;\n }\n try {\n return routeResolutionApi.resolve(routeRef)?.();\n } catch {\n // Route ref may require params not available in the current context\n return undefined;\n }\n}\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 configSchema: {\n path: z.string().optional(),\n title: z.string().optional(),\n },\n *factory(\n params: {\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 const resolvedTitle =\n title ?? node.spec.plugin.title ?? node.spec.plugin.pluginId;\n const resolvedIcon = icon ?? node.spec.plugin.icon;\n const titleRouteRef =\n (node.spec.plugin.routes as { root?: RouteRef }).root ?? params.routeRef;\n\n yield coreExtensionData.routePath(config.path ?? params.path);\n if (params.loader) {\n const loader = params.loader;\n const PageContent = () => {\n const routeResolutionApi = useApi(routeResolutionApiRef);\n const titleLink = resolveTitleLink(routeResolutionApi, titleRouteRef);\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n\n return (\n <PageLayout\n title={resolvedTitle}\n icon={resolvedIcon}\n noHeader={noHeader}\n titleLink={titleLink}\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: PageLayoutTab[] = 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 const routeResolutionApi = useApi(routeResolutionApiRef);\n const titleLink = resolveTitleLink(routeResolutionApi, titleRouteRef);\n\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n return (\n <PageLayout\n title={resolvedTitle}\n icon={resolvedIcon}\n tabs={tabs}\n titleLink={titleLink}\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 tabTitle = page.get(coreExtensionData.title);\n const element = page.get(coreExtensionData.reactElement);\n return (\n <Route\n key={index}\n path={`${path}/*`}\n element={\n <BreadcrumbEntry\n entry={{ label: tabTitle || path, href: path }}\n >\n {element}\n </BreadcrumbEntry>\n }\n />\n );\n })}\n </Routes>\n </PageLayout>\n );\n };\n\n yield coreExtensionData.reactElement(<PageContent />);\n } else {\n const PageContent = () => {\n const routeResolutionApi = useApi(routeResolutionApiRef);\n const titleLink = resolveTitleLink(routeResolutionApi, titleRouteRef);\n const headerActionsApi = useApi(pluginHeaderActionsApiRef);\n const headerActions = headerActionsApi.getPluginHeaderActions(pluginId);\n return (\n <PageLayout\n title={resolvedTitle}\n icon={resolvedIcon}\n titleLink={titleLink}\n headerActions={headerActions}\n />\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":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useRef, useEffect, useMemo } from 'react';
|
|
3
|
+
import { createVersionedContext, useVersionedContext, createVersionedValueMap } from '@backstage/version-bridge';
|
|
4
|
+
|
|
5
|
+
const CONTEXT_KEY = "breadcrumbs-context";
|
|
6
|
+
const DEPTH_KEY = "breadcrumbs-depth";
|
|
7
|
+
const DepthContext = createVersionedContext(DEPTH_KEY);
|
|
8
|
+
const EMPTY = { items: [] };
|
|
9
|
+
function useBreadcrumbEntries() {
|
|
10
|
+
const ctx = useVersionedContext(CONTEXT_KEY);
|
|
11
|
+
return ctx?.atVersion(1)?.breadcrumbs ?? EMPTY;
|
|
12
|
+
}
|
|
13
|
+
function BreadcrumbEntry(props) {
|
|
14
|
+
const ctx = useVersionedContext(CONTEXT_KEY);
|
|
15
|
+
const register = ctx?.atVersion(1)?.register;
|
|
16
|
+
const depthCtx = useVersionedContext(DEPTH_KEY);
|
|
17
|
+
const depth = depthCtx?.atVersion(1) ?? 0;
|
|
18
|
+
const { label, href } = props.entry;
|
|
19
|
+
const registrationRef = useRef(null);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!register) return void 0;
|
|
22
|
+
const reg = register({ label, href, depth });
|
|
23
|
+
registrationRef.current = reg;
|
|
24
|
+
return () => {
|
|
25
|
+
reg.unregister();
|
|
26
|
+
registrationRef.current = null;
|
|
27
|
+
};
|
|
28
|
+
}, [register, depth]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
registrationRef.current?.update(label, href);
|
|
31
|
+
}, [label, href]);
|
|
32
|
+
const depthValue = useMemo(
|
|
33
|
+
() => createVersionedValueMap({ 1: depth + 1 }),
|
|
34
|
+
[depth]
|
|
35
|
+
);
|
|
36
|
+
return /* @__PURE__ */ jsx(DepthContext.Provider, { value: depthValue, children: props.children });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { BreadcrumbEntry, useBreadcrumbEntries };
|
|
40
|
+
//# sourceMappingURL=useBreadcrumbEntries.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBreadcrumbEntries.esm.js","sources":["../../src/breadcrumbs/useBreadcrumbEntries.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 { useEffect, useMemo, useRef, type ReactNode } from 'react';\nimport {\n createVersionedContext,\n createVersionedValueMap,\n useVersionedContext,\n} from '@backstage/version-bridge';\nimport type { BreadcrumbEntryData } from './types';\n\ninterface Registration {\n update(label: string, href: string): void;\n unregister(): void;\n}\n\ninterface BreadcrumbsContextValue {\n breadcrumbs: { items: BreadcrumbEntryData[] };\n register: (entry: BreadcrumbEntryData) => Registration;\n}\n\n// This key must match the one used by BreadcrumbsRegistryProvider in @backstage/plugin-app.\nconst CONTEXT_KEY = 'breadcrumbs-context';\nconst DEPTH_KEY = 'breadcrumbs-depth';\n\ntype ContextMap = { 1: BreadcrumbsContextValue };\ntype DepthMap = { 1: number };\n\nconst DepthContext = createVersionedContext<DepthMap>(DEPTH_KEY);\n\nconst EMPTY: { items: BreadcrumbEntryData[] } = { items: [] };\n\n/**\n * Returns the current breadcrumb entries registered by page components anywhere\n * in the tree. Call this in components that render breadcrumbs (e.g. PluginHeader).\n *\n * @public\n */\nexport function useBreadcrumbEntries(): { items: BreadcrumbEntryData[] } {\n const ctx = useVersionedContext<ContextMap>(CONTEXT_KEY);\n return ctx?.atVersion(1)?.breadcrumbs ?? EMPTY;\n}\n\n/**\n * Registers a breadcrumb entry for the current page. The entry is added on\n * mount and removed on unmount. Wraps its children and increments the depth\n * counter so nested registrations are ordered correctly.\n *\n * @public\n */\nexport function BreadcrumbEntry(props: {\n entry: Omit<BreadcrumbEntryData, 'depth'>;\n children: ReactNode;\n}) {\n const ctx = useVersionedContext<ContextMap>(CONTEXT_KEY);\n const register = ctx?.atVersion(1)?.register;\n const depthCtx = useVersionedContext<DepthMap>(DEPTH_KEY);\n const depth = depthCtx?.atVersion(1) ?? 0;\n const { label, href } = props.entry;\n const registrationRef = useRef<Registration | null>(null);\n\n useEffect(() => {\n if (!register) return undefined;\n const reg = register({ label, href, depth });\n registrationRef.current = reg;\n return () => {\n reg.unregister();\n registrationRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [register, depth]);\n\n useEffect(() => {\n registrationRef.current?.update(label, href);\n }, [label, href]);\n\n const depthValue = useMemo(\n () => createVersionedValueMap({ 1: depth + 1 }),\n [depth],\n );\n\n return (\n <DepthContext.Provider value={depthValue}>\n {props.children}\n </DepthContext.Provider>\n );\n}\n"],"names":[],"mappings":";;;;AAmCA,MAAM,WAAA,GAAc,qBAAA;AACpB,MAAM,SAAA,GAAY,mBAAA;AAKlB,MAAM,YAAA,GAAe,uBAAiC,SAAS,CAAA;AAE/D,MAAM,KAAA,GAA0C,EAAE,KAAA,EAAO,EAAC,EAAE;AAQrD,SAAS,oBAAA,GAAyD;AACvE,EAAA,MAAM,GAAA,GAAM,oBAAgC,WAAW,CAAA;AACvD,EAAA,OAAO,GAAA,EAAK,SAAA,CAAU,CAAC,CAAA,EAAG,WAAA,IAAe,KAAA;AAC3C;AASO,SAAS,gBAAgB,KAAA,EAG7B;AACD,EAAA,MAAM,GAAA,GAAM,oBAAgC,WAAW,CAAA;AACvD,EAAA,MAAM,QAAA,GAAW,GAAA,EAAK,SAAA,CAAU,CAAC,CAAA,EAAG,QAAA;AACpC,EAAA,MAAM,QAAA,GAAW,oBAA8B,SAAS,CAAA;AACxD,EAAA,MAAM,KAAA,GAAQ,QAAA,EAAU,SAAA,CAAU,CAAC,CAAA,IAAK,CAAA;AACxC,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAK,GAAI,KAAA,CAAM,KAAA;AAC9B,EAAA,MAAM,eAAA,GAAkB,OAA4B,IAAI,CAAA;AAExD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,UAAU,OAAO,MAAA;AACtB,IAAA,MAAM,MAAM,QAAA,CAAS,EAAE,KAAA,EAAO,IAAA,EAAM,OAAO,CAAA;AAC3C,IAAA,eAAA,CAAgB,OAAA,GAAU,GAAA;AAC1B,IAAA,OAAO,MAAM;AACX,MAAA,GAAA,CAAI,UAAA,EAAW;AACf,MAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAAA,IAC5B,CAAA;AAAA,EAEF,CAAA,EAAG,CAAC,QAAA,EAAU,KAAK,CAAC,CAAA;AAEpB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,eAAA,CAAgB,OAAA,EAAS,MAAA,CAAO,KAAA,EAAO,IAAI,CAAA;AAAA,EAC7C,CAAA,EAAG,CAAC,KAAA,EAAO,IAAI,CAAC,CAAA;AAEhB,EAAA,MAAM,UAAA,GAAa,OAAA;AAAA,IACjB,MAAM,uBAAA,CAAwB,EAAE,CAAA,EAAG,KAAA,GAAQ,GAAG,CAAA;AAAA,IAC9C,CAAC,KAAK;AAAA,GACR;AAEA,EAAA,2BACG,YAAA,CAAa,QAAA,EAAb,EAAsB,KAAA,EAAO,UAAA,EAC3B,gBAAM,QAAA,EACT,CAAA;AAEJ;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2382,5 +2382,37 @@ declare const PluginHeaderActionBlueprint: _backstage_frontend_plugin_api.Extens
|
|
|
2382
2382
|
dataRefs: never;
|
|
2383
2383
|
}>;
|
|
2384
2384
|
|
|
2385
|
-
|
|
2386
|
-
|
|
2385
|
+
/**
|
|
2386
|
+
* A single breadcrumb entry registered by a page component.
|
|
2387
|
+
*
|
|
2388
|
+
* @public
|
|
2389
|
+
*/
|
|
2390
|
+
interface BreadcrumbEntryData {
|
|
2391
|
+
href: string;
|
|
2392
|
+
label: string;
|
|
2393
|
+
depth: number;
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
/**
|
|
2397
|
+
* Returns the current breadcrumb entries registered by page components anywhere
|
|
2398
|
+
* in the tree. Call this in components that render breadcrumbs (e.g. PluginHeader).
|
|
2399
|
+
*
|
|
2400
|
+
* @public
|
|
2401
|
+
*/
|
|
2402
|
+
declare function useBreadcrumbEntries(): {
|
|
2403
|
+
items: BreadcrumbEntryData[];
|
|
2404
|
+
};
|
|
2405
|
+
/**
|
|
2406
|
+
* Registers a breadcrumb entry for the current page. The entry is added on
|
|
2407
|
+
* mount and removed on unmount. Wraps its children and increments the depth
|
|
2408
|
+
* counter so nested registrations are ordered correctly.
|
|
2409
|
+
*
|
|
2410
|
+
* @public
|
|
2411
|
+
*/
|
|
2412
|
+
declare function BreadcrumbEntry(props: {
|
|
2413
|
+
entry: Omit<BreadcrumbEntryData, 'depth'>;
|
|
2414
|
+
children: ReactNode;
|
|
2415
|
+
}): react_jsx_runtime.JSX.Element;
|
|
2416
|
+
|
|
2417
|
+
export { AnalyticsContext, AnalyticsImplementationBlueprint, AnyApiFactory, AnyRouteRefParams, ApiBlueprint, ApiFactory, ApiHolder, ApiRef, AppNode, AppRootElementBlueprint, BreadcrumbEntry, ErrorDisplay, ExtensionBoundary, ExternalRouteRef, FeatureFlagState, FrontendFeature, FrontendPlugin, IconComponent, IconElement, NotFoundErrorPage, PageBlueprint, PageLayout, PluginHeaderActionBlueprint, Progress, RouteRef, SessionState, SubPageBlueprint, SubRouteRef, TypesToApiRefs, alertApiRef, analyticsApiRef, appLanguageApiRef, appThemeApiRef, atlassianAuthApiRef, bitbucketAuthApiRef, bitbucketServerAuthApiRef, configApiRef, coreExtensionData, createApiFactory, createApiRef, createFrontendFeatureLoader, createSwappableComponent, createTranslationMessages, createTranslationRef, createTranslationResource, dialogApiRef, discoveryApiRef, errorApiRef, featureFlagsApiRef, fetchApiRef, githubAuthApiRef, gitlabAuthApiRef, googleAuthApiRef, iconsApiRef, identityApiRef, microsoftAuthApiRef, oauthRequestApiRef, oktaAuthApiRef, oneloginAuthApiRef, openshiftAuthApiRef, pluginHeaderActionsApiRef, routeResolutionApiRef, storageApiRef, swappableComponentsApiRef, toastApiRef, translationApiRef, useAnalytics, useApi, useApiHolder, useAppNode, useBreadcrumbEntries, useRouteRef, useRouteRefParams, useTranslationRef, vmwareCloudAuthApiRef, withApis };
|
|
2418
|
+
export type { AlertApi, AlertMessage, AnalyticsApi, AnalyticsContextValue, AnalyticsEvent, AnalyticsEventAttributes, AnalyticsImplementation, AnalyticsImplementationFactory, AnalyticsTracker, ApiRefConfig, AppLanguageApi, AppTheme, AppThemeApi, AuthProviderInfo, AuthRequestOptions, BackstageIdentityApi, BackstageIdentityResponse, BackstageUserIdentity, BreadcrumbEntryData, ConfigApi, CreateFrontendFeatureLoaderOptions, CreateSwappableComponentOptions, DialogApi, DialogApiDialog, DiscoveryApi, ErrorApi, ErrorApiError, ErrorApiErrorContext, ErrorDisplayProps, ExtensionBoundaryProps, FeatureFlag, FeatureFlagsApi, FeatureFlagsSaveOptions, FetchApi, FrontendFeatureLoader, IconsApi, IdentityApi, NotFoundErrorPageProps, OAuthApi, OAuthRequestApi, OAuthRequester, OAuthRequesterOptions, OAuthScope, OpenIdConnectApi, PageLayoutProps, PageLayoutTab, PageTab, PendingOAuthRequest, PluginHeaderActionsApi, ProfileInfo, ProfileInfoApi, ProgressProps, RouteFunc, RouteResolutionApi, SessionApi, StorageApi, StorageValueSnapshot, SwappableComponentRef, SwappableComponentsApi, ToastApi, ToastApiMessage, ToastApiMessageLink, ToastApiPostResult, TranslationApi, TranslationFunction, TranslationMessages, TranslationMessagesOptions, TranslationRef, TranslationRefOptions, TranslationResource, TranslationResourceOptions, TranslationSnapshot };
|
package/dist/index.esm.js
CHANGED
|
@@ -46,6 +46,7 @@ export { createTranslationMessages } from './translation/TranslationMessages.esm
|
|
|
46
46
|
export { createTranslationResource } from './translation/TranslationResource.esm.js';
|
|
47
47
|
export { createTranslationRef } from './translation/TranslationRef.esm.js';
|
|
48
48
|
export { useTranslationRef } from './translation/useTranslationRef.esm.js';
|
|
49
|
+
export { BreadcrumbEntry, useBreadcrumbEntries } from './breadcrumbs/useBreadcrumbEntries.esm.js';
|
|
49
50
|
export { coreExtensionData } from './wiring/coreExtensionData.esm.js';
|
|
50
51
|
export { createExtension } from './wiring/createExtension.esm.js';
|
|
51
52
|
export { createExtensionInput } from './wiring/createExtensionInput.esm.js';
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/frontend-plugin-api",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3-next.1",
|
|
4
4
|
"backstage": {
|
|
5
5
|
"role": "web-library"
|
|
6
6
|
},
|
|
@@ -52,20 +52,20 @@
|
|
|
52
52
|
"test": "backstage-cli package test"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@backstage/config": "
|
|
56
|
-
"@backstage/errors": "
|
|
57
|
-
"@backstage/filter-predicates": "
|
|
58
|
-
"@backstage/types": "
|
|
59
|
-
"@backstage/version-bridge": "
|
|
55
|
+
"@backstage/config": "1.3.8",
|
|
56
|
+
"@backstage/errors": "1.3.1",
|
|
57
|
+
"@backstage/filter-predicates": "0.1.4-next.0",
|
|
58
|
+
"@backstage/types": "1.2.2",
|
|
59
|
+
"@backstage/version-bridge": "1.0.12",
|
|
60
60
|
"@standard-schema/spec": "^1.1.0",
|
|
61
61
|
"zod": "^4.0.0",
|
|
62
62
|
"zod-to-json-schema": "^3.25.1"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@backstage/cli": "
|
|
66
|
-
"@backstage/frontend-app-api": "
|
|
67
|
-
"@backstage/frontend-test-utils": "
|
|
68
|
-
"@backstage/test-utils": "
|
|
65
|
+
"@backstage/cli": "0.36.4-next.1",
|
|
66
|
+
"@backstage/frontend-app-api": "0.16.6-next.1",
|
|
67
|
+
"@backstage/frontend-test-utils": "0.6.2-next.0",
|
|
68
|
+
"@backstage/test-utils": "1.7.20-next.0",
|
|
69
69
|
"@testing-library/jest-dom": "^6.0.0",
|
|
70
70
|
"@testing-library/react": "^16.0.0",
|
|
71
71
|
"@types/react": "^18.0.0",
|