Add resizable panel width feature to template editor layout

Signed-off-by: Stephen Glass <stephen@stephen.glass>
This commit is contained in:
Stephen Glass
2025-07-29 23:13:04 -04:00
parent 02e3cd555b
commit b1c06969b6
6 changed files with 120 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.
+1
View File
@@ -97,6 +97,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
@@ -7584,6 +7584,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"
@@ -44334,6 +44335,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"