唯杰地图 logo唯杰地图
VJMAP
EN

侧边栏面板

vjcad/references/ui/sidebar-panel.md 10.5 KB198 行
原文

文档:vjcad/doc/ui/sidebar-panel.md 示例:

  • vjcad/example/ui/14sidebarpanel.js — 自定义面板
  • vjcad/example/ui/16menucustom.js — leftPanels / rightPanels 配置

1. 整体模型

vjcad 在主区域两侧各有一条 ActivityBar(图标栏),点击图标即激活右侧 / 左侧的 SidebarPanel。每个 SidebarPanel 是一个 Web Component,由 SidebarPanelManager 管理生命周期。

[ActivityBar 图标] → 激活 SidebarPanel → 渲染你写的 Web Component

侧边栏整体显示由 MainView({ sidebarStyle }) 控制:

sidebarStyle 含义
'right'(默认) 仅右侧
'left' 仅左侧
'both' 左右都显示
'none' 不显示侧边栏(仍可由命令打开)

2. 注册自己的面板

import { BasePanelComponent, registerSidebarPanel, activateSidebarPanel,
         IconRegistry, IconCategory, Engine, html, css } from 'vjcad';   // html/css/LitElement 直接从 'vjcad' 导出,没有 'vjcad/lit' 子路径

// 1. 写一个继承 BasePanelComponent 的 Web Component
class MyToolPanel extends BasePanelComponent {
    static styles = css`
        .toolbar { padding: 10px; }
        .btn { display: block; width: 100%; margin: 5px 0; padding: 8px;
               background: var(--theme-color-button-bg, #2d3748); color: var(--theme-color-font, #e8eaed);
               border: 1px solid var(--theme-color-border, #3d4a5c); cursor: pointer; }
        .btn:hover { background: var(--theme-color-button-hover-bg, #3d4a5c); }
    `;

    render() {
        return html`
            <div class="toolbar">
                <button class="btn" @click=${this._drawLine}>画直线</button>
                <button class="btn" @click=${this._drawCircle}>画圆</button>
                <button class="btn" @click=${() => Engine.zoomExtents()}>缩放全图</button>
            </div>`;
    }

    _drawLine()   { Engine.editor.executerWithOp('LINE'); }
    _drawCircle() { Engine.editor.executerWithOp('CIRCLE'); }

    // ⚠️ BasePanelComponent 上的 onActivate / onDeactivate / refresh 只是空钩子,框架从不调用;
    //    侧栏只把"当前面板"渲染进内容区,切换时面板元素被挂上 / 摘下 → 用 DOM 生命周期感知
    connectedCallback()    { super.connectedCallback();    /* 被切到(显示) */ }
    disconnectedCallback() { super.disconnectedCallback(); /* 被切走(隐藏) */ }
}
customElements.define('my-tool-panel', MyToolPanel);

// 2. 图标:icon 不是 URL,而是 images/<分类>/<名>.svg 形式的注册表键;先把 SVG 注册进 Actbar 分类
IconRegistry.registerIcon('my-tools', '<svg viewBox="0 0 24 24">...</svg>', { category: IconCategory.Actbar });

// 3. 注册到 ActivityBar(注册时立即创建面板实例)
registerSidebarPanel({
    name: 'my-tools',                          // 唯一标识(必须)
    label: '我的工具',                          // 面板标题(必须)
    icon: 'images/actbar/my-tools.svg',        // 解析为 Actbar 分类的 'my-tools' 图标
    position: 'left',                          // 可选 'left' | 'right',默认 'left'
    panelClass: MyToolPanel,                   // LitElement 子类(必须)
    tagName: 'my-tool-panel',                  // 上面已 define 过 → 必须传同一个 tagName
});

// 4. 立即激活
activateSidebarPanel('my-tools');

SidebarPanelConfig 字段(d.ts 完整定义,就这 7 个):

字段 必填 说明
name √ 唯一 ID
label √ 显示名
icon √ 不是 URL:须写成 images/<分类>/<名>.svg(通常 images/actbar/<名>.svg),按分类 + 名字去 IconRegistry 查;不会去加载文件,查不到就不显示图标
position 'left' / 'right',默认 'left'
panelClass √ LitElement 子类(不一定是 BasePanelComponent)
tagName 自定义元素标签(必须含 -;缺省自动生成 sidebar-panel-{name} 并 define)。同一个类只能 define 到一个标签:若你已 customElements.define 过该类,必须传同一个 tagName,否则自动 define 抛错 → registerPanel 返回 false
order ⚠️ 字段存在但不生效:排序读的是面板实例上的 order(从未赋值),实际按注册顺序排列

⚠️ activatedByCommand / defaultActive / minWidth / tooltip / data 等字段不在 SidebarPanelConfig 声明里, 运行时 registerPanel 也不会消费它们——不要依赖。

3. BasePanelComponent 生命周期

class BasePanelComponent extends LitElement {
    panelId: number;                     // 管理器分配的 id
    name: string;                        // = config.name
    caption: string;                     // = config.label
    label: string;                       // = config.label
    src: string;                         // = config.icon
    isActive: boolean;                   // 是否当前激活(普通字段,由侧栏直接赋值)
    onActivate(): void;                  // ⚠️ 空钩子,框架从不调用
    onDeactivate(): void;                // ⚠️ 空钩子,框架从不调用
    refresh(): void;                     // 默认 requestUpdate();框架不会自动调,需要时自己调
}

⚠️ 组件实例上没有 .config 成员(管理器只注入上面几个字段)。 想给面板传自定义数据,请在类里写死或通过模块作用域传入。

激活流程(实际行为):

  1. registerSidebarPanel 时就 document.createElement(tagName) 立即实例化,并注入 name / label / caption / src / panelId;视图已就绪则重建侧边栏
  2. 用户点击图标 / activateSidebarPanel(name) → 侧边栏只把各面板 isActive 置 false、目标面板置 true 并重绘
  3. onActivate() / onDeactivate() / refresh() 从不被框架调用(基类里只是空方法 / requestUpdate());侧栏内容区只渲染当前面板,切换时面板元素会被挂上 / 摘下,可用 connectedCallback / disconnectedCallback(记得调 super)感知;数据变了自己调 refresh()
  4. 注销(unregisterSidebarPanel):实例被移除(组件自身没有 dispose 回调);插件经 context.registerSidebarPanel 注册的面板在插件 unload 时移除

4. 实用 API

import {
    registerSidebarPanel, unregisterSidebarPanel,
    activateSidebarPanel,
    SidebarPanelManager
} from 'vjcad';
// 注意:没有 deactivateSidebarPanel / getActiveSidebarPanel 导出

registerSidebarPanel(config);
unregisterSidebarPanel('my-tools');
activateSidebarPanel('my-tools');         // = 用户点图标

// 直接拿管理器(高级)——⚠️ d.ts 注释声称的另一入口
// `Engine.sidebarPanelManager` 运行时是 undefined,只能走 getInstance()
const mgr = SidebarPanelManager.getInstance();
mgr.getRegisteredPanels();                // 无参,返回全部已注册 name[]
mgr.getPanelInstance('my-tools');         // 拿到实例(不是 getPanelComponent)
mgr.getLeftPanels(); mgr.getRightPanels();// 分左右列出
mgr.isPanelRegistered('my-tools');
mgr.unregisterPanelsByOwner('my-plugin'); // 插件卸载批量清理

## 5. 通过 URL 激活

启动时可用 `vcad_panel` query 参数激活指定面板(`MainView.URL_PARAMS.PANEL`):

?vcad_panel=my-tools


适合分享 / 嵌入场景。

## 6. MainView 配置内置面板

`leftPanels` / `rightPanels` 是**内置面板名的字符串数组**(不是对象配置):

```js
new MainView({
    leftPanels:  ['props', 'dsettings'],                       // 左侧只留这两个内置面板
    rightPanels: ['blocks', 'images', 'commands', 'alias', 'history'],
    autoActivatePropertyPanel: true   // 选中实体自动弹属性面板
});

可用内置面板名:props(属性)、dsettings(绘图设置)、blocks(块)、images(图像)、commands(命令)、alias(别名)、history(历史)等,详见 vjcad/doc/ui/sidebar-panel.md。 自定义面板请走 registerSidebarPanel,不能写进这两个数组。

7. 常见样式钩子(CSS 变量)

vjcad 没有 --button-bg / --text-color / --section-bg / --side-panel-bg-color / --side-panel-border-color 这类变量。想让面板跟随主题,在自己的 static styles 里直接用真实的全局主题变量(setThemeColors 写在 document.documentElement 上,可穿透 shadow DOM 继承):

变量 用途(ThemeColors 键)
--theme-color-sidebarframe-contents 侧栏内容底色(sidebarContentsBg)
--theme-color-font / --theme-color-font-secondary 主 / 次文字色(font / fontSecondary)
--theme-color-border 边框(border;别名 --border-color)
--theme-color-button-bg / --theme-color-button-hover-bg 按钮底色 / 悬停(buttonBg / buttonHoverBg)
--theme-color-input-bg 输入框底色(inputBg)
--theme-color-accent / --theme-hover 强调色 / 悬停高亮(accent / hover)

完整清单见导出常量 THEME_COLOR_CSS_VARS。自己起名的变量(如 --my-btn-bg)当然也能用,但它们只是你自己的约定,vjcad 不会设置。

红旗

  • customElements.define('my-tool-panel', ...) 的 tagName 必须包含 -,否则浏览器拒绝
  • 同一个 panelClass 想挂多个 name:必须每次都传同一个已 define 的 tagName(一个类只能对应一个标签名),之后每个 name 各有一个实例;内部状态请存在实例字段,别写到模块级全局
  • icon 不是 URL,必须是 images/<分类>/<名>.svg 且该名字已在对应分类注册(自定义图标先 IconRegistry.registerIcon(name, svg, { category: IconCategory.Actbar })),否则 ActivityBar 上看不见图标(看不到点不开就没法激活)
  • order 不生效,面板按注册顺序排列
  • leftPanels/rightPanels 数组里写的是内置面板名(如 'props'),不是中文标签,自定义面板也放不进去
  • autoActivatePropertyPanel: true 配合 sidebarStyle: 'none' 时无效(侧边栏被整体关闭了)

参考(官方文档 MCP)

  • 文档:vjcad/doc/ui/sidebar-panel.md
  • 示例:
  • vjcad/example/ui/14sidebarpanel.js — 自定义面板
  • vjcad/example/ui/16menucustom.js — leftPanels / rightPanels 配置