{"_id":"@bimangle/cesium-ui","name":"@bimangle/cesium-ui","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@bimangle/cesium-ui","version":"1.0.0","description":"A lightweight UI framework for Cesium viewer plugins, providing toolbar, notification stack, and future UI elements.","main":"dist/cesium-ui.js","module":"src/cesium-ui.js","scripts":{"build":"rollup -c","prepare":"npm run build"},"keywords":["cesium","ui","toolbar","notification","plugin","bimangle"],"author":{"name":"BimAngle"},"license":"MIT","publishConfig":{"access":"public"},"peerDependencies":{},"devDependencies":{"@rollup/plugin-commonjs":"^22.0.2","@rollup/plugin-node-resolve":"^13.3.0","rollup":"^2.79.2"},"_id":"@bimangle/cesium-ui@1.0.0","_nodeVersion":"24.11.0","_npmVersion":"11.6.1","dist":{"integrity":"sha512-cf+4e564bvNJ+X/9tKm5kk/LKxtbWlcdJFDiPP+J3DDLqy77c8LkQN4WH+vsHuEpyh4/qb8wQ6cpX5Mx1CLUCw==","shasum":"6db109eae55ef60151a8e6f851612667cb5a87ca","tarball":"https://registry.npmjs.org/@bimangle/cesium-ui/-/cesium-ui-1.0.0.tgz","fileCount":5,"unpackedSize":46942,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH1/as29cHV5Sm466SLrfP3TYGUH2Iu64k0X14cByQWFAiBk2I6mU1S68s9VMQHXivxqgsXZeAJXNQh6ToTea9P+Gw=="}]},"_npmUser":{"name":"bimangle.liu","email":"liuyongsheng@msn.com"},"directories":{},"maintainers":[{"name":"bimangle.liu","email":"liuyongsheng@msn.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cesium-ui_1.0.0_1783759926962_0.3627514667773011"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-11T08:52:06.827Z","1.0.0":"2026-07-11T08:52:07.095Z","modified":"2026-07-11T08:52:07.310Z"},"maintainers":[{"name":"bimangle.liu","email":"liuyongsheng@msn.com"}],"description":"A lightweight UI framework for Cesium viewer plugins, providing toolbar, notification stack, and future UI elements.","keywords":["cesium","ui","toolbar","notification","plugin","bimangle"],"author":{"name":"BimAngle"},"license":"MIT","readme":"# @bimangle/cesium-ui\n\nA lightweight UI framework for CesiumJS plugins, providing a shared **toolbar button strip** and **notification stack** — so multiple plugins can coexist on the same viewer without their buttons overlapping.\n\n面向 CesiumJS 插件的轻量级 UI 框架，提供统一的**工具栏按钮条**和**通知弹窗堆叠**机制，解决多插件按钮在 `viewer.container` 上相互覆盖的问题。\n\n---\n\n## Features / 特性\n\n- 🧩 **Pure DOM** — no Cesium runtime dependency; only `viewer.container` is used  \n  **纯 DOM 实现**，不依赖 Cesium 本身（仅使用 `viewer.container`）\n- ⚡ **Idempotent init** — safe to call `CesiumUIMixin` from multiple plugins; the framework initializes only once  \n  **幂等初始化**：多个插件调用 `CesiumUIMixin` 时，框架只初始化一次\n- 🔧 **Button registration** — plugins call `register()` to add buttons to the shared toolbar; buttons are laid out automatically  \n  **按钮注册机制**：各插件通过 `register()` 将工具按钮添加到共享工具条，自动排列不重叠\n- 🔔 **Notification stack** — multiple cards stack automatically; each card can be closed individually or all at once  \n  **通知弹窗堆叠**：支持多条弹窗自动叠放，可单独关闭或一键清除全部\n- 🌐 **Bilingual UI** — automatically uses Simplified Chinese or English based on `navigator.language`  \n  **双语界面**：根据 `navigator.language` 自动切换简体中文 / 英文\n\n---\n\n## Installation / 安装\n\n### NPM\n\n```bash\nnpm install @bimangle/cesium-ui\n```\n\n### CDN\n\n```html\n<!-- Cesium must be loaded first / 必须先加载 Cesium -->\n<script src=\"https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Cesium.js\"></script>\n<link href=\"https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Widgets/widgets.css\" rel=\"stylesheet\">\n\n<!-- Then include cesium-ui / 再引入 cesium-ui -->\n<script src=\"https://unpkg.com/@bimangle/cesium-ui/dist/cesium-ui.js\"></script>\n```\n\n---\n\n## Quick Start / 快速开始\n\n### Browser (CDN)\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <script src=\"https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Cesium.js\"></script>\n  <link href=\"https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Widgets/widgets.css\" rel=\"stylesheet\">\n  <script src=\"https://unpkg.com/@bimangle/cesium-ui/dist/cesium-ui.js\"></script>\n  <style>\n    html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }\n  </style>\n</head>\n<body>\n  <div id=\"cesiumContainer\"></div>\n  <script>\n    const viewer = new Cesium.Viewer('cesiumContainer');\n\n    // Initialize the UI framework.\n    // 初始化 UI 框架。\n    viewer.extend(CesiumUIMixin, {\n        position:             'top-left',      // toolbar position / 工具条位置\n        direction:            'vertical',      // toolbar direction / 工具条方向\n        notificationPosition: 'bottom-right',  // notification stack position / 通知弹窗位置\n    });\n\n    // Access the instance / 访问实例\n    // viewer.cesiumUI  → CesiumUI instance\n  </script>\n</body>\n</html>\n```\n\n### NPM / ES Modules\n\n```javascript\nimport * as Cesium from 'cesium';\nimport { CesiumUIMixin } from '@bimangle/cesium-ui';\n\nconst viewer = new Cesium.Viewer('cesiumContainer');\n\n// Optional — plugins will call this automatically with default options if omitted.\n// 可选：各插件会在需要时以默认参数自动调用，也可以提前手动指定配置。\nviewer.extend(CesiumUIMixin, {\n    position:             'top-left',      // toolbar position / 工具条位置\n    direction:            'vertical',      // toolbar direction / 工具条方向\n    notificationPosition: 'bottom-right',  // notification stack position / 通知弹窗位置\n});\n\n// Access the instance / 访问实例\n// viewer.cesiumUI  → CesiumUI instance\n```\n\n---\n\n## API Reference\n\n### `CesiumUIMixin(viewer, options?)`\n\nInitializes the UI framework and attaches a `CesiumUI` instance to `viewer.cesiumUI`.  \n**Idempotent** — if `viewer.cesiumUI` already exists the call is a no-op; subsequent `options` are ignored.\n\n初始化 UI 框架并将 `CesiumUI` 实例挂载到 `viewer.cesiumUI`。  \n**幂等**：若 `viewer.cesiumUI` 已存在，则直接返回，不重复初始化；后续传入的 `options` 会被忽略。\n\n| Parameter / 参数 | Type / 类型 | Default / 默认值 | Description / 说明 |\n|------------------|-------------|------------------|-------------------|\n| `viewer` | `Cesium.Viewer` | — | Target viewer / 目标 Viewer 实例 |\n| `options.position` | `'top-left'` \\| `'top-right'` \\| `'bottom-left'` \\| `'bottom-right'` | `'top-left'` | Toolbar corner / 工具条位置 |\n| `options.direction` | `'vertical'` \\| `'horizontal'` | `'vertical'` | Button layout direction / 工具条排列方向 |\n| `options.notificationPosition` | `'top-left'` \\| `'top-right'` \\| `'bottom-left'` \\| `'bottom-right'` | `'bottom-right'` | Notification stack corner / 通知弹窗堆叠位置 |\n| `options.panelCascadeDirection` | `'right'` \\| `'down'` | `'right'` | Direction to cascade sub-panels to avoid overlap / 子面板重叠时的错开方向 |\n\n---\n\n### `CesiumUI` — via `viewer.cesiumUI`\n\n#### Toolbar API / 工具条 API\n\n##### `register(descriptor) → ButtonHandle`\n\nRegisters a button on the toolbar. If the `id` already exists the existing button is replaced (hot-update safe).  \n向工具条注册一个按钮。若 `id` 已存在则覆盖（可用于热更新）。\n\n```javascript\nconst handle = viewer.cesiumUI.register({\n    id:      'my-tool',          // unique key / 唯一标识符\n    icon:    '🔧',               // icon character or text / 显示图标（Unicode 字符或文本）\n    title:   'My Tool',          // tooltip / 鼠标悬停提示\n    onClick: (handle) => { ... } // click callback / 点击回调，参数为 ButtonHandle\n});\n```\n\n##### `setActive(id, active)`\n\nSets the active (highlighted) state of a button.  \n设置按钮激活（高亮）状态。\n\n```javascript\nviewer.cesiumUI.setActive('my-tool', true);\n```\n\n##### `unregister(id)`\n\nRemoves a registered button.  \n移除已注册的按钮。\n\n```javascript\nviewer.cesiumUI.unregister('my-tool');\n```\n\n#### Notification API / 通知弹窗 API\n\n##### `notify(options) → NotificationHandle`\n\nPushes a notification card. New cards are appended at the bottom; older cards shift upward.  \nCards do not auto-dismiss — the user must close them manually.\n\n推送一条通知弹窗。新弹窗从底部追加，旧弹窗向上推移。弹窗不会自动消失，需用户手动关闭。\n\n```javascript\nconst notif = viewer.cesiumUI.notify({\n    title:   'Done',\n    content: '<b>Coordinate:</b> 30.12°N, 120.34°E',  // HTML string or HTMLElement\n    onClose: () => { /* optional close callback */ }\n});\n```\n\nWhen there are ≥ 2 cards, a **Clear All** button appears at the bottom of the stack.  \n当弹窗数量 ≥ 2 时，弹窗容器底部自动显示**清除全部**按钮。\n\n##### `clearNotifications()`\n\nCloses and removes all notification cards.  \n关闭并移除所有通知弹窗。\n\n```javascript\nviewer.cesiumUI.clearNotifications();\n```\n\n#### Toolbar helpers / 工具条辅助\n\n##### `getStripRect() → DOMRect`\n\nReturns `getBoundingClientRect()` of the toolbar strip element.  \nSub-panels can use this to position themselves next to the toolbar.\n\n返回工具条元素的 `getBoundingClientRect()` 结果，子面板可用此方法将自身定位到工具条旁边。\n\n##### `direction` / `position`\n\nRead-only properties returning the current direction and position strings.  \n只读属性，返回当前工具条的方向和位置字符串。\n\n#### Panel Registry API / 子面板注册 API\n\nPlugin sub-panels should register themselves when visible so the shared cascade logic can avoid overlaps.  \n插件子面板应在可见时进行注册，以便共享的错开逻辑避免重叠。\n\n##### `registerPanel(el)`\n\nRegisters a visible panel element.  \n注册一个当前可见的子面板元素。\n\n##### `unregisterPanel(el)`\n\nUnregisters a panel element (call when the panel is hidden or destroyed).  \n取消注册（面板隐藏或销毁时调用）。\n\n##### `findFreePosition(left, top, panelEl) → {left, top}`\n\nStarting from the given `(left, top)` offset (relative to the viewer container), shifts the position along `panelCascadeDirection` in 8 px steps until the given `panelEl` no longer overlaps any registered panel.  \nReturns the adjusted `{left, top}`.\n\n以给定的 `(left, top)`（相对于容器）为起点，沿 `panelCascadeDirection` 方向以 8 px 为步长逐步偏移，直到不与已注册的其他子面板发生重叠。返回调整后的 `{left, top}`。\n\n```javascript\nviewer.extend(CesiumUIMixin, { panelCascadeDirection: 'right' });\n\n// In your plugin's show() method:\nconst pos = viewer.cesiumUI.findFreePosition(initialLeft, initialTop, myPanelEl);\nmyPanelEl.style.left = pos.left + 'px';\nmyPanelEl.style.top  = pos.top  + 'px';\nviewer.cesiumUI.registerPanel(myPanelEl);\n\n// In your plugin's hide() method:\nviewer.cesiumUI.unregisterPanel(myPanelEl);\n```\n\n#### Lifecycle / 生命周期\n\n##### `destroy()`\n\nDestroys the UI framework, removes all DOM elements, and clears internal state.  \n销毁 UI 框架，移除所有 DOM 元素并清理内部状态。\n\n---\n\n### `ButtonHandle`\n\nReturn value of `register()`.  \n`register()` 的返回值。\n\n| Member / 成员 | Description / 说明 |\n|---------------|-------------------|\n| `id` | Unique button key (read-only) / 按钮唯一标识符（只读） |\n| `setActive(active)` | Shorthand for `cesiumUI.setActive(id, active)` |\n| `remove()` | Shorthand for `cesiumUI.unregister(id)` |\n\n---\n\n### `NotificationHandle`\n\nReturn value of `notify()`.  \n`notify()` 的返回值。\n\n| Member / 成员 | Description / 说明 |\n|---------------|-------------------|\n| `close()` | Closes and removes this notification card / 关闭并移除该条通知 |\n\n---\n\n## Visual Layout / 视觉样式\n\nToolbar (`direction: 'vertical'`, `position: 'top-left'`):  \n工具条（`direction: 'vertical'`，`position: 'top-left'`）：\n\n```\n┌──┐  ← top: 8px, left: 8px\n│🌲│  ← scene-tree button\n├──┤\n│📐│  ← measure button (border highlights when active / 激活时边框高亮)\n└──┘\n```\n\nNotification stack (`notificationPosition: 'bottom-right'`, newest card at bottom):  \n通知弹窗堆叠（`notificationPosition: 'bottom-right'`，新弹窗在底部）：\n\n```\n┌──────────────────────────────┬───┐\n│ 📍 Coordinate Result         │ × │\n├──────────────────────────────┴───┤\n│  Latitude:   30.12345678°        │\n│  Longitude: 120.12345678°        │\n│  Height:        45.2341 m        │\n└──────────────────────────────────┘\n┌──────────────────────────────┬───┐\n│ 📐 Distance Result           │ × │\n├──────────────────────────────┴───┤\n│  Distance:   123.46 m            │\n└──────────────────────────────────┘\n            [ Clear All ]\n```\n\n---\n\n## Plugin Developer Guide / 插件开发者接入指南\n\nThis package is the shared infrastructure for all BimAngle CesiumJS plugins.  \n本包设计为所有 BimAngle CesiumJS 插件的共享基础设施。\n\n```javascript\nfunction MyPluginMixin(viewer, options) {\n    // Safe to call even if already initialized — idempotent.\n    // 幂等调用，已初始化时安全忽略。\n    viewer.extend(CesiumUIMixin);\n\n    const handle = viewer.cesiumUI.register({\n        id:      'my-plugin',\n        icon:    '🔧',\n        title:   'My Plugin',\n        onClick: () => myPanel.toggle()\n    });\n\n    let _userMoved = false;\n\n    function expand() {\n        handle.setActive(true);\n        if (!_userMoved) {\n            // Position next to toolbar, then cascade to avoid other panels.\n            // 先定位到工具条旁，再错开已有面板。\n            const stripRect = viewer.cesiumUI.getStripRect();\n            const cRect     = viewer.container.getBoundingClientRect();\n            const initLeft  = stripRect.right - cRect.left + 6;\n            const initTop   = stripRect.top   - cRect.top;\n            const pos = viewer.cesiumUI.findFreePosition(initLeft, initTop, myPanelEl);\n            myPanelEl.style.left = pos.left + 'px';\n            myPanelEl.style.top  = pos.top  + 'px';\n        }\n        viewer.cesiumUI.registerPanel(myPanelEl);\n    }\n\n    function collapse() {\n        handle.setActive(false);\n        viewer.cesiumUI.unregisterPanel(myPanelEl);\n    }\n\n    function destroy() {\n        handle.remove();\n        viewer.cesiumUI.unregisterPanel(myPanelEl);\n    }\n}\n\n// Mount the plugin / 挂载插件\nviewer.extend(MyPluginMixin);\n```\n\n---\n\n## CSS Class Reference / CSS 命名规范\n\nAll CSS classes use the `ba-ctb-` prefix (`ba` = BimAngle, `ctb` = cesium-toolbar).  \n所有 CSS 类名采用 `ba-ctb-` 前缀（`ba` = BimAngle，`ctb` = cesium-toolbar）。\n\n| Class / 类名 | Purpose / 用途 |\n|-------------|---------------|\n| `.ba-ctb-strip` | Toolbar container / 工具条容器 |\n| `.ba-ctb-btn` | Single toolbar button / 单个工具按钮 |\n| `.ba-ctb-btn--active` | Active (highlighted) button state / 按钮激活态 |\n| `.ba-ctb-notification-stack` | Notification stack container / 通知弹窗堆叠容器 |\n| `.ba-ctb-notification` | Single notification card / 单条通知卡片 |\n| `.ba-ctb-notification-header` | Card title bar / 通知标题栏 |\n| `.ba-ctb-notification-body` | Card content area / 通知内容区 |\n| `.ba-ctb-notification-close` | Per-card close button / 单条关闭按钮 |\n| `.ba-ctb-notification-clear-all` | Clear-all button / 清除全部按钮 |\n\n---\n\n## Compatibility / 兼容性\n\n- Chrome 90+, Firefox 88+, Edge 90+\n- No Cesium version constraint (DOM-only)  \n  无 Cesium 版本限制（纯 DOM 实现）\n\n---\n\n## Build / 构建\n\n```bash\nnpm install\nnpm run build\n# Output: dist/cesium-ui.js\n```\n\n---\n\n## License / 许可证\n\nMIT © BimAngle\n","readmeFilename":"README.md","_rev":"1-8256396e185a7ba65390805b9fabab25"}