Просмотр исходного кода

feat(panel-manager-plugin): panel add auto resize (#880)

* feat(panel-manager-plugin): panel auto resize

* feat(panel-manager-plugin): fix z-index

* feat(panel-manager-plugin): simplify panelManager.close
xiamidaxia 3 месяцев назад
Родитель
Сommit
15f4bb2fd5
23 измененных файлов с 204 добавлено и 44 удалено
  1. 3 2
      apps/demo-fixed-layout/src/components/sidebar/sidebar-renderer.tsx
  2. 1 1
      apps/demo-fixed-layout/src/components/tools/styles.tsx
  3. 1 1
      apps/demo-fixed-layout/src/form-components/form-header/index.tsx
  4. 3 2
      apps/demo-free-layout/src/components/problem-panel/problem-panel.tsx
  5. 3 2
      apps/demo-free-layout/src/components/sidebar/node-form-panel.tsx
  6. 1 1
      apps/demo-free-layout/src/components/sidebar/sidebar-node-renderer.tsx
  7. 1 1
      apps/demo-free-layout/src/components/testrun/testrun-panel/index.module.less
  8. 2 1
      apps/demo-free-layout/src/components/testrun/testrun-panel/test-run-panel.tsx
  9. 1 1
      apps/demo-free-layout/src/components/tools/styles.tsx
  10. 1 1
      apps/demo-free-layout/src/form-components/form-header/index.tsx
  11. 4 1
      apps/docs/src/en/guide/advanced/plugin/panel-manager-plugin.mdx
  12. 7 4
      apps/docs/src/zh/guide/advanced/plugin/panel-manager-plugin.mdx
  13. 8 1
      packages/plugins/panel-manager-plugin/src/components/panel-layer/css.ts
  14. 22 3
      packages/plugins/panel-manager-plugin/src/components/panel-layer/float-panel.tsx
  15. 7 6
      packages/plugins/panel-manager-plugin/src/components/panel-layer/panel-layer.tsx
  16. 80 0
      packages/plugins/panel-manager-plugin/src/components/resize-bar/index.tsx
  17. 2 4
      packages/plugins/panel-manager-plugin/src/create-panel-manager-plugin.ts
  18. 2 0
      packages/plugins/panel-manager-plugin/src/index.ts
  19. 26 2
      packages/plugins/panel-manager-plugin/src/services/float-panel.ts
  20. 6 0
      packages/plugins/panel-manager-plugin/src/services/panel-config.ts
  21. 17 6
      packages/plugins/panel-manager-plugin/src/services/panel-layer.ts
  22. 4 4
      packages/plugins/panel-manager-plugin/src/services/panel-manager.ts
  23. 2 0
      packages/plugins/panel-manager-plugin/src/types.ts

+ 3 - 2
apps/demo-fixed-layout/src/components/sidebar/sidebar-renderer.tsx

@@ -27,7 +27,7 @@ export const SidebarRenderer: React.FC<NodeFormPanelProps> = ({ nodeId }) => {
   const handleClose = useCallback(() => {
     // Sidebar delayed closing
     startTransition(() => {
-      panelManager.close(nodeFormPanelFactory.key, 'right');
+      panelManager.close(nodeFormPanelFactory.key);
     });
   }, []);
   const node = nodeId ? document.getNode(nodeId) : undefined;
@@ -64,7 +64,7 @@ export const SidebarRenderer: React.FC<NodeFormPanelProps> = ({ nodeId }) => {
   useEffect(() => {
     if (node) {
       const toDispose = node.onDispose(() => {
-        panelManager.close(nodeFormPanelFactory.key, 'right');
+        panelManager.close(nodeFormPanelFactory.key);
       });
       return () => toDispose.dispose();
     }
@@ -90,5 +90,6 @@ export const SidebarRenderer: React.FC<NodeFormPanelProps> = ({ nodeId }) => {
 
 export const nodeFormPanelFactory: PanelFactory<NodeFormPanelProps> = {
   key: 'node-form-panel',
+  defaultSize: 400,
   render: (props: NodeFormPanelProps) => <SidebarRenderer {...props} />,
 };

+ 1 - 1
apps/demo-fixed-layout/src/components/tools/styles.tsx

@@ -14,7 +14,7 @@ export const ToolContainer = styled.div`
   pointer-events: none;
   gap: 8px;
 
-  z-index: 99;
+  z-index: 20;
 `;
 
 export const ToolSection = styled.div`

+ 1 - 1
apps/demo-fixed-layout/src/form-components/form-header/index.tsx

@@ -76,7 +76,7 @@ export function FormHeader() {
     e.stopPropagation(); // Disable clicking prevents the sidebar from opening
   };
   const handleClose = () => {
-    panelManager.close(nodeFormPanelFactory.key, 'right');
+    panelManager.close(nodeFormPanelFactory.key);
   };
 
   return (

+ 3 - 2
apps/demo-free-layout/src/components/problem-panel/problem-panel.tsx

@@ -15,7 +15,7 @@ export const ProblemPanel = () => {
     <div
       style={{
         width: '100%',
-        height: '200px',
+        height: '100%',
         borderRadius: '8px',
         background: 'rgb(251, 251, 251)',
         border: '1px solid rgba(82,100,154, 0.13)',
@@ -26,7 +26,7 @@ export const ProblemPanel = () => {
           type="tertiary"
           theme="borderless"
           icon={<IconClose />}
-          onClick={() => panelManager.close(PROBLEM_PANEL, 'bottom')}
+          onClick={() => panelManager.close(PROBLEM_PANEL)}
         />
       </div>
       <div>problem panel</div>
@@ -36,6 +36,7 @@ export const ProblemPanel = () => {
 
 export const problemPanelFactory: PanelFactory<void> = {
   key: PROBLEM_PANEL,
+  defaultSize: 200,
   render: () => <ProblemPanel />,
 };
 

+ 3 - 2
apps/demo-free-layout/src/components/sidebar/node-form-panel.tsx

@@ -27,7 +27,7 @@ export const NodeFormPanel: React.FC<NodeFormPanelProps> = ({ nodeId }) => {
   const handleClose = useCallback(() => {
     // Sidebar delayed closing
     startTransition(() => {
-      panelManager.close(nodeFormPanelFactory.key, 'right');
+      panelManager.close(nodeFormPanelFactory.key);
     });
   }, []);
   const node = document.getNode(nodeId);
@@ -64,7 +64,7 @@ export const NodeFormPanel: React.FC<NodeFormPanelProps> = ({ nodeId }) => {
   useEffect(() => {
     if (node) {
       const toDispose = node.onDispose(() => {
-        panelManager.close(nodeFormPanelFactory.key, 'right');
+        panelManager.close(nodeFormPanelFactory.key);
       });
       return () => toDispose.dispose();
     }
@@ -90,5 +90,6 @@ export const NodeFormPanel: React.FC<NodeFormPanelProps> = ({ nodeId }) => {
 
 export const nodeFormPanelFactory: PanelFactory<NodeFormPanelProps> = {
   key: 'node-form-panel',
+  defaultSize: 500,
   render: (props: NodeFormPanelProps) => <NodeFormPanel {...props} />,
 };

+ 1 - 1
apps/demo-free-layout/src/components/sidebar/sidebar-node-renderer.tsx

@@ -17,7 +17,7 @@ export function SidebarNodeRenderer(props: { node: FlowNodeEntity }) {
         style={{
           background: 'rgb(251, 251, 251)',
           height: '100%',
-          width: '500px',
+          width: '100%',
           borderRadius: 8,
           border: '1px solid rgba(82,100,154, 0.13)',
           boxSizing: 'border-box',

+ 1 - 1
apps/demo-free-layout/src/components/testrun/testrun-panel/index.module.less

@@ -86,7 +86,7 @@
   background: rgb(255, 255, 255);
   border-radius: 8px;
   height: 100%;
-  width: 400px;
+  width: 100%;
   border: 1px solid rgba(82, 100, 154, 0.13);
   padding: 8px 0 8px 0;
   display: flex;

+ 2 - 1
apps/demo-free-layout/src/components/testrun/testrun-panel/test-run-panel.tsx

@@ -65,7 +65,7 @@ export const TestRunSidePanel: FC<TestRunSidePanelProps> = () => {
     await runtimeService.taskCancel();
     setValues({});
     setRunning(false);
-    panelManager.close(testRunPanelFactory.key, 'right');
+    panelManager.close(testRunPanelFactory.key);
   };
 
   const renderRunning = (
@@ -157,5 +157,6 @@ export const TestRunSidePanel: FC<TestRunSidePanelProps> = () => {
 
 export const testRunPanelFactory: PanelFactory<TestRunSidePanelProps> = {
   key: 'test-run-panel',
+  defaultSize: 400,
   render: () => <TestRunSidePanel />,
 };

+ 1 - 1
apps/demo-free-layout/src/components/tools/styles.tsx

@@ -16,7 +16,7 @@ export const ToolContainer = styled.div`
   pointer-events: none;
   gap: 8px;
 
-  z-index: 99;
+  z-index: 20;
 `;
 
 export const ToolSection = styled.div`

+ 1 - 1
apps/demo-free-layout/src/form-components/form-header/index.tsx

@@ -33,7 +33,7 @@ export function FormHeader() {
     ctx.get<CommandService>(CommandService).executeCommand(FlowCommandId.DELETE, [node]);
   };
   const handleClose = () => {
-    panelManager.close(nodeFormPanelFactory.key, 'right');
+    panelManager.close(nodeFormPanelFactory.key);
   };
   useEffect(() => {
     // 折叠 loop 子节点

+ 4 - 1
apps/docs/src/en/guide/advanced/plugin/panel-manager-plugin.mdx

@@ -50,6 +50,7 @@ import { type PanelFactory } from '@flowgram.ai/panel-manager-plugin';
 export const NODE_FORM_PANEL = 'node-form-panel';
 export const nodeFormPanelFactory: PanelFactory<NodeFormPanelProps> = {
   key: NODE_FORM_PANEL,
+  defaultSize: 400,
   render: (props: NodeFormPanelProps) => <NodeFormPanel {...props} />
 }
 ```
@@ -59,6 +60,8 @@ Pass the defined object into the plugin:
 ```ts
 createPanelManagerPlugin({
   factories: [nodeFormPanelFactory]
+  getPopupContainer: (ctx) => ctx.playground.node.parentNode,
+  autoResize: true
 })
 ```
 
@@ -80,7 +83,7 @@ class NodeFormService {
     })
   }
   closePanel() {
-    this.panelManager.close(NODE_FORM_PANEL, 'right')
+    this.panelManager.close(NODE_FORM_PANEL)
   }
 }
 ```

+ 7 - 4
apps/docs/src/zh/guide/advanced/plugin/panel-manager-plugin.mdx

@@ -44,21 +44,24 @@ return (
 
 这里以节点表单面板为例:
 
-```tsx
+```tsx pure
 import { type PanelFactory } from '@flowgram.ai/panel-manager-plugin';
 
 export const NODE_FORM_PANEL = 'node-form-panel';
 export const nodeFormPanelFactory: PanelFactory<NodeFormPanelProps> = {
   key: NODE_FORM_PANEL,
+  defaultSize: 400,
   render: (props: NodeFormPanelProps) => <NodeFormPanel {...props} />
 }
 ```
 
 将定义好的对象传入插件中:
 
-```ts
+```ts pure
 createPanelManagerPlugin({
-  factories: [nodeFormPanelFactory]
+  factories: [nodeFormPanelFactory],
+  getPopupContainer: (ctx) => ctx.playground.node.parentNode,
+  autoResize: true
 })
 ```
 
@@ -80,7 +83,7 @@ class NodeFormService {
     })
   }
   closePanel() {
-    this.panelManager.close(NODE_FORM_PANEL, 'right')
+    this.panelManager.close(NODE_FORM_PANEL)
   }
 }
 ```

+ 8 - 1
packages/plugins/panel-manager-plugin/src/components/panel-layer/css.ts

@@ -3,6 +3,12 @@
  * SPDX-License-Identifier: MIT
  */
 
+export const globalCSS = `
+  .gedit-flow-panel-layer * {
+    box-sizing: border-box;
+  }
+`;
+
 export const panelLayer: React.CSSProperties = {
   pointerEvents: 'none',
   position: 'absolute',
@@ -15,6 +21,7 @@ export const panelLayer: React.CSSProperties = {
   height: '100%',
   padding: '4px',
   boxSizing: 'border-box',
+  overflow: 'hidden',
 };
 
 export const leftArea: React.CSSProperties = {
@@ -33,7 +40,6 @@ export const rightArea: React.CSSProperties = {
   flexGrow: 1,
   flexShrink: 0,
   minWidth: 0,
-  overflowY: 'auto',
 
   display: 'flex',
   columnGap: '4px',
@@ -59,4 +65,5 @@ export const floatPanelWrap: React.CSSProperties = {
   pointerEvents: 'auto',
   height: '100%',
   width: '100%',
+  overflow: 'auto',
 };

+ 22 - 3
packages/plugins/panel-manager-plugin/src/components/panel-layer/float-panel.tsx

@@ -3,11 +3,12 @@
  * SPDX-License-Identifier: MIT
  */
 
-import { useEffect, useRef, startTransition, useState } from 'react';
+import { useEffect, useRef, startTransition, useState, useCallback } from 'react';
 
 import { Area } from '../../types';
 import { usePanelManager } from '../../hooks/use-panel-manager';
 import { floatPanelWrap } from './css';
+import { ResizeBar } from '../resize-bar';
 
 export const FloatPanel: React.FC<{ area: Area }> = ({ area }) => {
   const [, setVersion] = useState(0);
@@ -15,7 +16,7 @@ export const FloatPanel: React.FC<{ area: Area }> = ({ area }) => {
   const panel = useRef(panelManager.getPanel(area));
   const render = () =>
     panel.current.elements.map((i) => (
-      <div className="float-panel-wrap" key={i.key} style={floatPanelWrap}>
+      <div className="float-panel-wrap" key={i.key} style={{ ...floatPanelWrap, ...i.style }}>
         {i.el}
       </div>
     ));
@@ -30,6 +31,24 @@ export const FloatPanel: React.FC<{ area: Area }> = ({ area }) => {
     });
     return () => dispose.dispose();
   }, [panel]);
+  const onResize = useCallback((newSize: number) => panel.current!.updateSize(newSize), []);
+  const size = panel.current!.currentSize;
+  const sizeStyle =
+    area === 'right' ? { width: size, height: '100%' } : { height: size, width: '100%' };
 
-  return <>{node.current}</>;
+  return (
+    <div
+      className="gedit-flow-panel"
+      style={{
+        position: 'relative',
+        display: panel.current.visible ? 'block' : 'none',
+        ...sizeStyle,
+      }}
+    >
+      {panelManager.config.autoResize && (
+        <ResizeBar size={size} isVertical={area === 'right'} onResize={onResize} />
+      )}
+      {node.current}
+    </div>
+  );
 };

+ 7 - 6
packages/plugins/panel-manager-plugin/src/components/panel-layer/panel-layer.tsx

@@ -4,19 +4,20 @@
  */
 
 import { FloatPanel } from './float-panel';
-import { panelLayer, leftArea, rightArea, mainArea, bottomArea } from './css';
+import { panelLayer, leftArea, rightArea, mainArea, bottomArea, globalCSS } from './css';
 
 export const PanelLayer: React.FC<React.PropsWithChildren> = ({ children }) => (
-  <div className="panel-layer" style={panelLayer}>
-    <div className="left-area" style={leftArea}>
-      <div className="main-area" style={mainArea}>
+  <div style={panelLayer}>
+    <style dangerouslySetInnerHTML={{ __html: globalCSS }} />
+    <div className="gedit-flow-panel-left-area" style={leftArea}>
+      <div className="gedit-flow-panel-main-area" style={mainArea}>
         {children}
       </div>
-      <div className="bottom-area" style={bottomArea}>
+      <div className="gedit-flow-panel-bottom-area" style={bottomArea}>
         <FloatPanel area="bottom" />
       </div>
     </div>
-    <div className="right-area" style={rightArea}>
+    <div className="gedit-flow-panel-right-area" style={rightArea}>
       <FloatPanel area="right" />
     </div>
   </div>

+ 80 - 0
packages/plugins/panel-manager-plugin/src/components/resize-bar/index.tsx

@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import React, { useRef, useState } from 'react';
+
+interface Props {
+  onResize: (w: number) => void;
+  size: number;
+  isVertical?: boolean;
+}
+
+export const ResizeBar: React.FC<Props> = ({ onResize, size, isVertical }) => {
+  const currentPoint = useRef<null | number>(null);
+  const [isDragging, setIsDragging] = useState(false);
+  const [isHovered, setIsHovered] = useState(false);
+  return (
+    <div
+      onMouseDown={(e) => {
+        currentPoint.current = isVertical ? e.clientX : e.clientY;
+        e.stopPropagation();
+        e.preventDefault();
+        setIsDragging(true);
+        const mouseUp = () => {
+          currentPoint.current = null;
+          document.body.removeEventListener('mouseup', mouseUp);
+          document.body.removeEventListener('mousemove', mouseMove);
+          setIsDragging(false);
+        };
+        const mouseMove = (e: MouseEvent) => {
+          const delta = currentPoint.current! - (isVertical ? e.clientX : e.clientY);
+          onResize(size + delta);
+        };
+        document.body.addEventListener('mouseup', mouseUp);
+        document.body.addEventListener('mousemove', mouseMove);
+      }}
+      onMouseEnter={() => setIsHovered(true)}
+      onMouseLeave={() => setIsHovered(false)}
+      style={{
+        position: 'absolute',
+        top: 0,
+        left: 0,
+        zIndex: 999,
+        display: 'flex',
+        alignItems: 'center',
+        justifyContent: 'center',
+        pointerEvents: 'auto',
+        ...(isVertical
+          ? {
+              cursor: 'ew-resize',
+              height: '100%',
+              marginLeft: -5,
+              width: 10,
+            }
+          : {
+              cursor: 'ns-resize',
+              width: '100%',
+              marginTop: -5,
+              height: 10,
+            }),
+      }}
+    >
+      <div
+        style={{
+          ...(isVertical
+            ? {
+                width: 3,
+                height: '100%',
+              }
+            : {
+                height: 3,
+                width: '100%',
+              }),
+          backgroundColor: isDragging || isHovered ? 'var(--g-playground-line)' : 'transparent',
+        }}
+      />
+    </div>
+  );
+};

+ 2 - 4
packages/plugins/panel-manager-plugin/src/create-panel-manager-plugin.ts

@@ -9,14 +9,12 @@ import { defineConfig } from './services/panel-config';
 import { PanelManager, PanelManagerConfig, PanelLayer } from './services';
 
 export const createPanelManagerPlugin = definePluginCreator<Partial<PanelManagerConfig>>({
-  onBind: ({ bind }) => {
+  onBind: ({ bind }, opt) => {
     bind(PanelManager).to(PanelManager).inSingletonScope();
-    bind(PanelManagerConfig).toConstantValue(defineConfig({}));
+    bind(PanelManagerConfig).toConstantValue(defineConfig(opt));
   },
   onInit(ctx, opt) {
     ctx.playground.registerLayer(PanelLayer);
-    const config = defineConfig(opt);
-    ctx.container.rebind(PanelManagerConfig).toConstantValue(config);
     const panelManager = ctx.container.get<PanelManager>(PanelManager);
     panelManager.init();
   },

+ 2 - 0
packages/plugins/panel-manager-plugin/src/index.ts

@@ -12,5 +12,7 @@ export { PanelManager } from './services';
 /** react hooks */
 export { usePanelManager } from './hooks/use-panel-manager';
 
+export { ResizeBar } from './components/resize-bar';
+
 /** types */
 export type { Area, PanelFactory } from './types';

+ 26 - 2
packages/plugins/panel-manager-plugin/src/services/float-panel.ts

@@ -9,25 +9,45 @@ import type { PanelFactory, PanelConfig } from '../types';
 
 export interface PanelElement {
   key: string;
+  style?: React.CSSProperties;
   el: React.ReactNode;
 }
 
+const PANEL_SIZE_DEFAULT = 400;
+
 export class FloatPanel {
   elements: PanelElement[] = [];
 
   private onUpdateEmitter = new Emitter<void>();
 
+  sizeMap = new Map<string, number>();
+
   onUpdate = this.onUpdateEmitter.event;
 
+  currentFactoryKey = '';
+
+  updateSize(newSize: number) {
+    this.sizeMap.set(this.currentFactoryKey, newSize);
+    this.onUpdateEmitter.fire();
+  }
+
+  get currentSize(): number {
+    return this.sizeMap.get(this.currentFactoryKey) || PANEL_SIZE_DEFAULT;
+  }
+
   constructor(private config: PanelConfig) {}
 
   open(factory: PanelFactory<any>, options: any) {
     const el = factory.render(options?.props);
     const idx = this.elements.findIndex((e) => e.key === factory.key);
+    this.currentFactoryKey = factory.key;
+    if (!this.sizeMap.has(factory.key)) {
+      this.sizeMap.set(factory.key, factory.defaultSize || PANEL_SIZE_DEFAULT);
+    }
     if (idx >= 0) {
-      this.elements[idx] = { el, key: factory.key };
+      this.elements[idx] = { el, key: factory.key, style: factory.style };
     } else {
-      this.elements.push({ el, key: factory.key });
+      this.elements.push({ el, key: factory.key, style: factory.style });
       if (this.elements.length > this.config.max) {
         this.elements.shift();
       }
@@ -35,6 +55,10 @@ export class FloatPanel {
     this.onUpdateEmitter.fire();
   }
 
+  get visible() {
+    return this.elements.length > 0;
+  }
+
   close(key?: string) {
     if (!key) {
       this.elements = [];

+ 6 - 0
packages/plugins/panel-manager-plugin/src/services/panel-config.ts

@@ -3,12 +3,16 @@
  * SPDX-License-Identifier: MIT
  */
 
+import { PluginContext } from '@flowgram.ai/core';
+
 import type { PanelFactory, PanelConfig } from '../types';
 
 export interface PanelManagerConfig {
   factories: PanelFactory<any>[];
   right: PanelConfig;
   bottom: PanelConfig;
+  getPopupContainer: (ctx: PluginContext) => HTMLElement; // default playground.node.parentElement
+  autoResize: boolean;
 }
 
 export const PanelManagerConfig = Symbol('PanelManagerConfig');
@@ -22,6 +26,8 @@ export const defineConfig = (config: Partial<PanelManagerConfig>) => {
       max: 1,
     },
     factories: [],
+    getPopupContainer: (ctx: PluginContext) => ctx.playground.node.parentNode as HTMLElement,
+    autoResize: true,
   };
   return {
     ...defaultConfig,

+ 17 - 6
packages/plugins/panel-manager-plugin/src/services/panel-layer.ts

@@ -6,20 +6,31 @@
 import ReactDOM from 'react-dom';
 import { createElement } from 'react';
 
-import { injectable } from 'inversify';
-import { domUtils } from '@flowgram.ai/utils';
-import { Layer } from '@flowgram.ai/core';
+import { injectable, inject } from 'inversify';
+import { domUtils, Disposable } from '@flowgram.ai/utils';
+import { Layer, PluginContext } from '@flowgram.ai/core';
 
 import { PanelLayer as PanelLayerComp } from '../components/panel-layer';
+import { PanelManagerConfig } from './panel-config';
 
 @injectable()
 export class PanelLayer extends Layer {
-  panelRoot = domUtils.createDivWithClass('gedit-flow-panel-layer');
+  @inject(PanelManagerConfig) private readonly panelConfig: PanelManagerConfig;
+
+  @inject(PluginContext) private readonly pluginContext: PluginContext;
+
+  readonly panelRoot = domUtils.createDivWithClass('gedit-flow-panel-layer');
 
   layout: JSX.Element | null = null;
 
   onReady(): void {
-    this.playgroundNode.parentNode?.appendChild(this.panelRoot);
+    this.panelConfig.getPopupContainer(this.pluginContext).appendChild(this.panelRoot);
+    this.toDispose.push(
+      Disposable.create(() => {
+        // Remove from PopupContainer
+        this.panelRoot.remove();
+      })
+    );
     const commonStyle = {
       pointerEvents: 'none',
       width: '100%',
@@ -27,7 +38,7 @@ export class PanelLayer extends Layer {
       position: 'absolute',
       left: 0,
       top: 0,
-      zIndex: 20,
+      zIndex: 100,
     };
     domUtils.setStyle(this.panelRoot, commonStyle);
   }

+ 4 - 4
packages/plugins/panel-manager-plugin/src/services/panel-manager.ts

@@ -11,7 +11,7 @@ import { FloatPanel } from './float-panel';
 
 @injectable()
 export class PanelManager {
-  @inject(PanelManagerConfig) private readonly config: PanelManagerConfig;
+  @inject(PanelManagerConfig) readonly config: PanelManagerConfig;
 
   readonly panelRegistry = new Map<string, PanelFactory<any>>();
 
@@ -38,9 +38,9 @@ export class PanelManager {
     panel.open(factory, options);
   }
 
-  close(key: string, area: Area = 'right') {
-    const panel = this.getPanel(area);
-    panel.close(key);
+  close(key?: string) {
+    this.right.close(key);
+    this.bottom.close(key);
   }
 
   getPanel(area: Area) {

+ 2 - 0
packages/plugins/panel-manager-plugin/src/types.ts

@@ -12,5 +12,7 @@ export interface PanelConfig {
 
 export interface PanelFactory<T extends any> {
   key: string;
+  defaultSize: number;
+  style?: React.CSSProperties;
   render: (props: T) => React.ReactNode;
 }