Merge branch 'backstage:master' into master
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ApiBlueprint,
|
||||
createApiRef,
|
||||
createExtensionInput,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { AddonBlueprint } from '@backstage/plugin-techdocs-react/alpha';
|
||||
import { TechDocsAddonOptions } from '@backstage/plugin-techdocs-react';
|
||||
|
||||
interface TechDocsAddonsApi {
|
||||
getAddons(): TechDocsAddonOptions[];
|
||||
}
|
||||
|
||||
export const techdocsAddonsApiRef = createApiRef<TechDocsAddonsApi>({
|
||||
id: 'plugin.techdocs.addons',
|
||||
});
|
||||
|
||||
export const TechDocsAddonsApiExtension = ApiBlueprint.makeWithOverrides({
|
||||
name: 'addons',
|
||||
inputs: {
|
||||
addons: createExtensionInput([AddonBlueprint.dataRefs.addon]),
|
||||
},
|
||||
factory(originalFactory, { inputs }) {
|
||||
const addons = inputs.addons.map(output =>
|
||||
output.get(AddonBlueprint.dataRefs.addon),
|
||||
);
|
||||
return originalFactory(defineParams =>
|
||||
defineParams({
|
||||
api: techdocsAddonsApiRef,
|
||||
deps: {},
|
||||
factory: () => ({
|
||||
getAddons: () => addons,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Suspense } from 'react';
|
||||
import LibraryBooks from '@material-ui/icons/LibraryBooks';
|
||||
import {
|
||||
createFrontendPlugin,
|
||||
@@ -34,7 +35,11 @@ import {
|
||||
EntityIconLinkBlueprint,
|
||||
} from '@backstage/plugin-catalog-react/alpha';
|
||||
import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';
|
||||
import { AddonBlueprint } from '@backstage/plugin-techdocs-react/alpha';
|
||||
import {
|
||||
AddonBlueprint,
|
||||
attachTechDocsAddonComponentData,
|
||||
} from '@backstage/plugin-techdocs-react/alpha';
|
||||
import { TechDocsAddonsApiExtension, techdocsAddonsApiRef } from './addonsApi';
|
||||
import { TechDocsClient, TechDocsStorageClient } from '../client';
|
||||
import {
|
||||
rootCatalogDocsRouteRef,
|
||||
@@ -42,7 +47,6 @@ import {
|
||||
rootRouteRef,
|
||||
} from '../routes';
|
||||
import { TechDocsReaderLayout } from '../reader';
|
||||
import { attachTechDocsAddonComponentData } from '@backstage/plugin-techdocs-react/alpha';
|
||||
import {
|
||||
TechDocsAddons,
|
||||
techdocsApiRef,
|
||||
@@ -152,24 +156,46 @@ const techDocsReaderPage = PageBlueprint.makeWithOverrides({
|
||||
inputs: {
|
||||
addons: createExtensionInput([AddonBlueprint.dataRefs.addon]),
|
||||
},
|
||||
factory(originalFactory, { inputs }) {
|
||||
const addons = inputs.addons.map(output => {
|
||||
const options = output.get(AddonBlueprint.dataRefs.addon);
|
||||
const Addon = options.component;
|
||||
attachTechDocsAddonComponentData(Addon, options);
|
||||
return <Addon key={options.name} />;
|
||||
});
|
||||
config: {
|
||||
schema: {
|
||||
withoutSearch: z => z.boolean().default(false),
|
||||
withoutHeader: z => z.boolean().default(false),
|
||||
},
|
||||
},
|
||||
factory(originalFactory, { apis, inputs, config }) {
|
||||
const addonsApi = apis.get(techdocsAddonsApiRef);
|
||||
|
||||
return originalFactory({
|
||||
path: '/docs/:namespace/:kind/:name',
|
||||
routeRef: rootDocsRouteRef,
|
||||
loader: async () =>
|
||||
await import('../Router').then(({ TechDocsReaderRouter }) => (
|
||||
loader: async () => {
|
||||
// Merge addons from the API with old-style direct attachments
|
||||
const apiAddons = addonsApi?.getAddons() ?? [];
|
||||
const directAddons = inputs.addons.map(output =>
|
||||
output.get(AddonBlueprint.dataRefs.addon),
|
||||
);
|
||||
const addonOptions = [...apiAddons, ...directAddons];
|
||||
|
||||
const addons = addonOptions.map(options => {
|
||||
const Addon = options.component;
|
||||
attachTechDocsAddonComponentData(Addon, options);
|
||||
return (
|
||||
<Suspense key={options.name} fallback={null}>
|
||||
<Addon />
|
||||
</Suspense>
|
||||
);
|
||||
});
|
||||
|
||||
return import('../Router').then(({ TechDocsReaderRouter }) => (
|
||||
<TechDocsReaderRouter>
|
||||
<TechDocsReaderLayout />
|
||||
<TechDocsReaderLayout
|
||||
withSearch={!config.withoutSearch}
|
||||
withHeader={!config.withoutHeader}
|
||||
/>
|
||||
<TechDocsAddons>{addons}</TechDocsAddons>
|
||||
</TechDocsReaderRouter>
|
||||
)),
|
||||
));
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -191,29 +217,41 @@ const techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({
|
||||
),
|
||||
},
|
||||
factory(originalFactory, context) {
|
||||
const addonsApi = context.apis.get(techdocsAddonsApiRef);
|
||||
|
||||
return originalFactory(
|
||||
{
|
||||
path: 'docs',
|
||||
title: 'TechDocs',
|
||||
routeRef: rootCatalogDocsRouteRef,
|
||||
loader: () =>
|
||||
import('../Router').then(({ EmbeddedDocsRouter }) => {
|
||||
const addons = context.inputs.addons.map(output => {
|
||||
const options = output.get(AddonBlueprint.dataRefs.addon);
|
||||
const Addon = options.component;
|
||||
attachTechDocsAddonComponentData(Addon, options);
|
||||
return <Addon key={options.name} />;
|
||||
});
|
||||
loader: () => {
|
||||
// Merge addons from the API with old-style direct attachments
|
||||
const apiAddons = addonsApi?.getAddons() ?? [];
|
||||
const directAddons = context.inputs.addons.map(output =>
|
||||
output.get(AddonBlueprint.dataRefs.addon),
|
||||
);
|
||||
const addonOptions = [...apiAddons, ...directAddons];
|
||||
|
||||
const addons = addonOptions.map(options => {
|
||||
const Addon = options.component;
|
||||
attachTechDocsAddonComponentData(Addon, options);
|
||||
return (
|
||||
<EmbeddedDocsRouter
|
||||
emptyState={context.inputs.emptyState?.get(
|
||||
coreExtensionData.reactElement,
|
||||
)}
|
||||
>
|
||||
<TechDocsAddons>{addons}</TechDocsAddons>
|
||||
</EmbeddedDocsRouter>
|
||||
<Suspense key={options.name} fallback={null}>
|
||||
<Addon />
|
||||
</Suspense>
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
return import('../Router').then(({ EmbeddedDocsRouter }) => (
|
||||
<EmbeddedDocsRouter
|
||||
emptyState={context.inputs.emptyState?.get(
|
||||
coreExtensionData.reactElement,
|
||||
)}
|
||||
>
|
||||
<TechDocsAddons>{addons}</TechDocsAddons>
|
||||
</EmbeddedDocsRouter>
|
||||
));
|
||||
},
|
||||
},
|
||||
context,
|
||||
);
|
||||
@@ -244,6 +282,7 @@ export default createFrontendPlugin({
|
||||
extensions: [
|
||||
techDocsClientApi,
|
||||
techDocsStorageApi,
|
||||
TechDocsAddonsApiExtension,
|
||||
techDocsNavItem,
|
||||
techDocsPage,
|
||||
techDocsReaderPage,
|
||||
|
||||
@@ -67,7 +67,12 @@ const CopyToClipboardButton = ({ text }: CopyToClipboardButtonProps) => {
|
||||
leaveDelay={1000}
|
||||
>
|
||||
<IconButton
|
||||
style={{ position: 'absolute' }}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
// top & right was removed from upstream .md-clipboard in mkdocs-material 9.7.0
|
||||
top: '0.5rem',
|
||||
right: '0.5rem',
|
||||
}}
|
||||
className="md-clipboard md-icon"
|
||||
onClick={handleClick}
|
||||
aria-label="Copy to clipboard"
|
||||
@@ -81,6 +86,8 @@ const CopyToClipboardButton = ({ text }: CopyToClipboardButtonProps) => {
|
||||
/**
|
||||
* Recreates copy-to-clipboard functionality attached to <code> snippets that
|
||||
* is native to mkdocs-material theme.
|
||||
*
|
||||
* Unlike native mkdocs-material theme, this is always enabled and does not respect the mkdocs's config `theme.features` `content.code.copy` setting.
|
||||
*/
|
||||
export const copyToClipboard = (theme: Theme): Transformer => {
|
||||
return dom => {
|
||||
|
||||
@@ -81,15 +81,12 @@ export default ({ theme, sidebar }: RuleOptions) => `
|
||||
bottom: 75px;
|
||||
position: fixed;
|
||||
width: 16rem;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-color: rgb(193, 193, 193) #eee;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
.md-sidebar .md-sidebar__scrollwrap {
|
||||
width: calc(16rem);
|
||||
overflow-y: hidden;
|
||||
height: 100%
|
||||
}
|
||||
|
||||
@supports selector(::-webkit-scrollbar) {
|
||||
[dir=ltr] .md-sidebar__inner {
|
||||
padding-right: calc(100% - 15.1rem);
|
||||
@@ -98,28 +95,6 @@ export default ({ theme, sidebar }: RuleOptions) => `
|
||||
.md-sidebar--secondary {
|
||||
right: ${theme.spacing(3)}px;
|
||||
}
|
||||
.md-sidebar::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
.md-sidebar::-webkit-scrollbar-button {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
.md-sidebar::-webkit-scrollbar-track {
|
||||
background: #eee;
|
||||
border: 1 px solid rgb(250, 250, 250);
|
||||
box-shadow: 0px 0px 3px #dfdfdf inset;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.md-sidebar::-webkit-scrollbar-thumb {
|
||||
width: 5px;
|
||||
background: rgb(193, 193, 193);
|
||||
border: transparent;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.md-sidebar::-webkit-scrollbar-thumb:hover {
|
||||
background: rgb(125, 125, 125);
|
||||
}
|
||||
|
||||
.md-content {
|
||||
max-width: calc(100% - 16rem * 2);
|
||||
@@ -155,6 +130,8 @@ export default ({ theme, sidebar }: RuleOptions) => `
|
||||
@media screen and (min-width: 76.25em) {
|
||||
.md-sidebar {
|
||||
height: auto;
|
||||
/* Less padding before the Previous / Next buttons */
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ ${headings.reduce<string>((style, heading) => {
|
||||
|
||||
.md-typeset table:not([class]) {
|
||||
font-size: var(--md-typeset-font-size);
|
||||
border: 1px solid var(--md-default-fg-color);
|
||||
border: 1px solid var(--md-typeset-table-color);
|
||||
border-bottom: none;
|
||||
border-collapse: collapse;
|
||||
border-radius: ${theme.shape.borderRadius}px;
|
||||
@@ -119,7 +119,7 @@ ${headings.reduce<string>((style, heading) => {
|
||||
font-weight: bold;
|
||||
}
|
||||
.md-typeset table:not([class]) td, .md-typeset table:not([class]) th {
|
||||
border-bottom: 1px solid var(--md-default-fg-color);
|
||||
border-bottom: 1px solid var(--md-typeset-table-color);
|
||||
}
|
||||
|
||||
.md-typeset pre > code::-webkit-scrollbar-thumb {
|
||||
|
||||
@@ -28,11 +28,8 @@ export default ({ theme }: RuleOptions) => `
|
||||
/* FONT */
|
||||
--md-default-fg-color: ${theme.palette.text.primary};
|
||||
--md-default-fg-color--light: ${theme.palette.text.secondary};
|
||||
--md-default-fg-color--lighter: ${lighten(theme.palette.text.secondary, 0.7)};
|
||||
--md-default-fg-color--lightest: ${lighten(
|
||||
theme.palette.text.secondary,
|
||||
0.3,
|
||||
)};
|
||||
--md-default-fg-color--lighter: ${alpha(theme.palette.text.secondary, 0.3)};
|
||||
--md-default-fg-color--lightest: ${alpha(theme.palette.text.secondary, 0.15)};
|
||||
|
||||
/* BACKGROUND */
|
||||
--md-default-bg-color:${theme.palette.background.default};
|
||||
@@ -146,7 +143,7 @@ export default ({ theme }: RuleOptions) => `
|
||||
--md-typeset-font-size: 1rem;
|
||||
--md-typeset-color: var(--md-default-fg-color);
|
||||
--md-typeset-a-color: ${theme.palette.link};
|
||||
--md-typeset-table-color: ${theme.palette.text.primary};
|
||||
--md-typeset-table-color: ${alpha(theme.palette.text.primary, 0.15)};
|
||||
--md-typeset-table-color--light: ${alpha(theme.palette.text.primary, 0.05)};
|
||||
--md-typeset-del-color: ${
|
||||
theme.palette.type === 'dark'
|
||||
|
||||
Reference in New Issue
Block a user