Merge pull request #30456 from stephenglass/feat/add-adjustable-panels-template-editor

feat(scaffolder): adjustable panels width in template editor layouts
This commit is contained in:
Ben Lambert
2025-08-11 10:27:44 +02:00
committed by GitHub
7 changed files with 121 additions and 34 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': minor
---
Add resizable panels width for the editor and preview panels in the template editor and template form playground layouts. Users can now resize these panels by dragging the divider between the two areas.
@@ -552,3 +552,4 @@ zod
Zolotusky
zoomable
zsh
resizable
+1
View File
@@ -96,6 +96,7 @@
"luxon": "^3.0.0",
"qs": "^6.9.4",
"react-resizable": "^3.0.5",
"react-resizable-panels": "^3.0.4",
"react-use": "^17.2.4",
"react-window": "^1.8.10",
"yaml": "^2.0.0",
@@ -34,6 +34,7 @@ import {
TemplateEditorLayoutFiles,
TemplateEditorLayoutPreview,
TemplateEditorLayoutConsole,
TemplateEditorPanels,
} from './TemplateEditorLayout';
import { TemplateEditorToolbar } from './TemplateEditorToolbar';
import { TemplateEditorToolbarFileMenu } from './TemplateEditorToolbarFileMenu';
@@ -88,17 +89,24 @@ export const TemplateEditor = (props: {
<TemplateEditorLayoutBrowser>
<TemplateEditorBrowser onClose={closeDirectory} />
</TemplateEditorLayoutBrowser>
<TemplateEditorLayoutFiles>
<TemplateEditorTextArea.DirectoryEditor errorText={errorText} />
</TemplateEditorLayoutFiles>
<TemplateEditorLayoutPreview>
<TemplateEditorForm.DirectoryEditorDryRun
setErrorText={setErrorText}
fieldExtensions={fieldExtensions}
layouts={layouts}
formProps={formProps}
/>
</TemplateEditorLayoutPreview>
<TemplateEditorPanels
autoSaveId="template-editor"
files={
<TemplateEditorLayoutFiles>
<TemplateEditorTextArea.DirectoryEditor errorText={errorText} />
</TemplateEditorLayoutFiles>
}
preview={
<TemplateEditorLayoutPreview>
<TemplateEditorForm.DirectoryEditorDryRun
setErrorText={setErrorText}
fieldExtensions={fieldExtensions}
layouts={layouts}
formProps={formProps}
/>
</TemplateEditorLayoutPreview>
}
/>
<TemplateEditorLayoutConsole>
<DryRunResults />
</TemplateEditorLayoutConsole>
@@ -14,8 +14,11 @@
* limitations under the License.
*/
import { PropsWithChildren } from 'react';
import { PropsWithChildren, ReactNode } from 'react';
import { WithStyles, withStyles } from '@material-ui/core/styles';
import { PanelGroup, Panel, PanelResizeHandle } from 'react-resizable-panels';
import { useTheme } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
export const TemplateEditorLayout = withStyles(
theme => ({
@@ -36,7 +39,7 @@ export const TemplateEditorLayout = withStyles(
"browser editor preview"
"results results results"
`,
gridTemplateColumns: '1fr 3fr 2fr',
gridTemplateColumns: '1fr 5fr',
gridTemplateRows: 'auto 1fr auto',
},
},
@@ -73,12 +76,15 @@ export const TemplateEditorLayoutBrowser = withStyles(
));
export const TemplateEditorLayoutFiles = withStyles(
{
theme => ({
root: {
gridArea: 'editor',
overflow: 'auto',
[theme.breakpoints.up('md')]: {
height: '100%',
},
},
},
}),
{ name: 'ScaffolderTemplateEditorLayoutFiles' },
)(({ children, classes }: PropsWithChildren<WithStyles>) => (
<section className={classes.root}>{children}</section>
@@ -91,7 +97,7 @@ export const TemplateEditorLayoutPreview = withStyles(
position: 'relative',
backgroundColor: theme.palette.background.default,
[theme.breakpoints.up('md')]: {
borderLeft: `1px solid ${theme.palette.divider}`,
height: '100%',
},
},
scroll: {
@@ -124,3 +130,50 @@ export const TemplateEditorLayoutConsole = withStyles(
)(({ children, classes }: PropsWithChildren<WithStyles>) => (
<section className={classes.root}>{children}</section>
));
export const TemplateEditorPanelResizeHandle = withStyles(
{
root: {
width: 8,
cursor: 'col-resize',
background: 'rgba(0,0,0,0.04)',
},
},
{ name: 'ScaffolderTemplateEditorPanelResizeHandle' },
)(({ classes }: { classes: WithStyles['classes'] }) => (
<PanelResizeHandle className={classes.root} />
));
export function TemplateEditorPanels({
files,
preview,
autoSaveId = 'template-editor-panels',
}: {
files: ReactNode;
preview: ReactNode;
autoSaveId?: string;
}) {
const theme = useTheme();
const isMdUp = useMediaQuery(theme.breakpoints.up('md'));
if (isMdUp) {
return (
<PanelGroup direction="horizontal" autoSaveId={autoSaveId}>
<Panel minSize={15} defaultSize={50}>
{files}
</Panel>
<TemplateEditorPanelResizeHandle />
<Panel minSize={15} defaultSize={50}>
{preview}
</Panel>
</PanelGroup>
);
}
// Stack as rows for small screens, just render children in a plain block
return (
<>
{files}
{preview}
</>
);
}
@@ -39,6 +39,7 @@ import {
TemplateEditorLayoutToolbar,
TemplateEditorLayoutFiles,
TemplateEditorLayoutPreview,
TemplateEditorPanels,
} from './TemplateEditorLayout';
import { TemplateEditorToolbar } from './TemplateEditorToolbar';
import { TemplateEditorToolbarFileMenu } from './TemplateEditorToolbarFileMenu';
@@ -113,7 +114,7 @@ const useStyles = makeStyles(
"textArea preview"
`,
gridTemplateRows: 'auto 1fr',
gridTemplateColumns: '1fr 1fr',
gridTemplateColumns: '1fr',
},
},
files: {
@@ -207,23 +208,30 @@ export const TemplateFormPreviewer = ({
/>
</TemplateEditorToolbar>
</TemplateEditorLayoutToolbar>
<TemplateEditorLayoutFiles classes={{ root: classes.files }}>
<TemplateEditorTextArea
content={templateYaml}
onUpdate={setTemplateYaml}
errorText={errorText}
/>
</TemplateEditorLayoutFiles>
<TemplateEditorLayoutPreview>
<TemplateEditorForm
content={templateYaml}
contentIsSpec
fieldExtensions={customFieldExtensions}
setErrorText={setErrorText}
layouts={layouts}
formProps={formProps}
/>
</TemplateEditorLayoutPreview>
<TemplateEditorPanels
autoSaveId="template-form-previewer"
files={
<TemplateEditorLayoutFiles classes={{ root: classes.files }}>
<TemplateEditorTextArea
content={templateYaml}
onUpdate={setTemplateYaml}
errorText={errorText}
/>
</TemplateEditorLayoutFiles>
}
preview={
<TemplateEditorLayoutPreview>
<TemplateEditorForm
content={templateYaml}
contentIsSpec
fieldExtensions={customFieldExtensions}
setErrorText={setErrorText}
layouts={layouts}
formProps={formProps}
/>
</TemplateEditorLayoutPreview>
}
/>
</TemplateEditorLayout>
);
};
+11
View File
@@ -7675,6 +7675,7 @@ __metadata:
react: "npm:^18.0.2"
react-dom: "npm:^18.0.2"
react-resizable: "npm:^3.0.5"
react-resizable-panels: "npm:^3.0.4"
react-router-dom: "npm:^6.3.0"
react-use: "npm:^17.2.4"
react-window: "npm:^1.8.10"
@@ -44498,6 +44499,16 @@ __metadata:
languageName: node
linkType: hard
"react-resizable-panels@npm:^3.0.4":
version: 3.0.4
resolution: "react-resizable-panels@npm:3.0.4"
peerDependencies:
react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
checksum: 10/92b8ac224d3c60c7c8ea0d80f712d9cd719afad76d4f1a5b8c8cc7c886b77f1d5058fa622ea057f28f5f7fc1a2c86e38e14d7b0f33722c30bff02e37f9fba705
languageName: node
linkType: hard
"react-resizable@npm:^3.0.4, react-resizable@npm:^3.0.5":
version: 3.0.5
resolution: "react-resizable@npm:3.0.5"