Files
bim_engine/src/components/section-axis-panel/index.ts
2025-12-24 19:02:34 +08:00

301 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import './index.css';
import type { ThemeConfig } from '../../themes/types';
import { IBimComponent } from '../../types/component';
import { localeManager, t } from '../../services/locale';
import { themeManager } from '../../services/theme';
import type { SectionAxisPanelOptions, SectionAxis } from './types';
/**
* 轴向剖切面板组件
* 第一行隐藏toggle、反向
* 第二行X、Y、Z互斥按钮组
*/
export class SectionAxisPanel implements IBimComponent {
public element: HTMLElement;
private options: SectionAxisPanelOptions;
// 状态
private isHidden: boolean = false;
private activeAxis: SectionAxis = 'x';
// DOM 引用 - 第一行
private hideBtn!: HTMLButtonElement;
private reverseBtn!: HTMLButtonElement;
private hideLabelEl!: HTMLElement;
private reverseLabelEl!: HTMLElement;
// DOM 引用 - 第二行
private axisXBtn!: HTMLButtonElement;
private axisYBtn!: HTMLButtonElement;
private axisZBtn!: HTMLButtonElement;
// 订阅清理
private unsubscribeLocale: (() => void) | null = null;
private unsubscribeTheme: (() => void) | null = null;
constructor(options: SectionAxisPanelOptions = {}) {
this.options = options;
this.isHidden = options.defaultHidden ?? false;
this.activeAxis = options.defaultAxis ?? 'x';
this.element = this.createDom();
}
/**
* 初始化组件
*/
public init(): void {
// 订阅语言变更
this.unsubscribeLocale = localeManager.subscribe(() => {
this.setLocales();
});
// 订阅主题变更
this.unsubscribeTheme = themeManager.subscribe((theme) => {
this.setTheme(theme);
});
// 初始应用
this.setLocales();
this.setTheme(themeManager.getTheme());
// 初始化按钮状态
this.updateHideButtonState();
this.updateAxisButtonsState();
}
/**
* 设置主题
*/
public setTheme(theme: ThemeConfig): void {
const style = this.element.style;
style.setProperty('--bim-section-axis-btn-bg', theme.componentBackground ?? 'rgba(255, 255, 255, 0.06)');
style.setProperty('--bim-section-axis-btn-hover', theme.componentHover ?? 'rgba(255, 255, 255, 0.10)');
style.setProperty('--bim-section-axis-btn-active', theme.componentActive ?? 'rgba(255, 255, 255, 0.14)');
style.setProperty('--bim-primary-color', theme.primary ?? '#1890ff');
style.setProperty('--bim-icon-color', theme.icon ?? '#ccc');
style.setProperty('--bim-text-color', theme.textSecondary ?? 'rgba(255, 255, 255, 0.90)');
style.setProperty('--bim-text-active-color', theme.textPrimary ?? '#fff');
}
/**
* 设置语言
*/
public setLocales(): void {
this.hideLabelEl.textContent = t('sectionAxis.actions.hide');
this.reverseLabelEl.textContent = t('sectionAxis.actions.reverse');
// XYZ按钮的文字不需要国际化保持为单个字母
this.hideBtn.title = t('sectionAxis.actions.hide');
this.reverseBtn.title = t('sectionAxis.actions.reverse');
this.axisXBtn.title = t('sectionAxis.actions.axisX');
this.axisYBtn.title = t('sectionAxis.actions.axisY');
this.axisZBtn.title = t('sectionAxis.actions.axisZ');
}
/**
* 设置隐藏状态
*/
public setHiddenState(isHidden: boolean): void {
this.isHidden = isHidden;
this.updateHideButtonState();
}
/**
* 获取隐藏状态
*/
public getHiddenState(): boolean {
return this.isHidden;
}
/**
* 设置激活的轴向
*/
public setActiveAxis(axis: SectionAxis): void {
this.activeAxis = axis;
this.updateAxisButtonsState();
}
/**
* 获取激活的轴向
*/
public getActiveAxis(): SectionAxis {
return this.activeAxis;
}
/**
* 销毁组件
*/
public destroy(): void {
if (this.unsubscribeLocale) {
this.unsubscribeLocale();
this.unsubscribeLocale = null;
}
if (this.unsubscribeTheme) {
this.unsubscribeTheme();
this.unsubscribeTheme = null;
}
this.element.remove();
}
/**
* 创建 DOM
*/
private createDom(): HTMLElement {
const root = document.createElement('div');
root.className = 'section-axis-panel';
// 第一行:隐藏、反向
const row1 = document.createElement('div');
row1.className = 'section-axis-row-1';
this.hideBtn = this.createButton(
'hide',
'<svg viewBox="0 0 24 24"><path fill="currentColor" d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46A11.804 11.804 0 0 0 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></svg>',
() => this.handleHideToggle()
);
this.reverseBtn = this.createButton(
'reverse',
'<svg viewBox="0 0 24 24"><path fill="currentColor" d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/></svg>',
() => this.handleReverse()
);
row1.appendChild(this.hideBtn);
row1.appendChild(this.reverseBtn);
// 第二行X、Y、Z
const row2 = document.createElement('div');
row2.className = 'section-axis-row-2';
this.axisXBtn = this.createAxisButton('axisX', 'X', () => this.handleAxisChange('x'));
this.axisYBtn = this.createAxisButton('axisY', 'Y', () => this.handleAxisChange('y'));
this.axisZBtn = this.createAxisButton('axisZ', 'Z', () => this.handleAxisChange('z'));
row2.appendChild(this.axisXBtn);
row2.appendChild(this.axisYBtn);
row2.appendChild(this.axisZBtn);
root.appendChild(row1);
root.appendChild(row2);
return root;
}
/**
* 创建按钮(带图标)
*/
private createButton(
type: 'hide' | 'reverse',
iconSvg: string,
onClick: () => void
): HTMLButtonElement {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'section-axis-btn';
// 图标
const icon = document.createElement('div');
icon.className = 'section-axis-btn-icon';
icon.innerHTML = iconSvg;
btn.appendChild(icon);
// 标签
const label = document.createElement('div');
label.className = 'section-axis-btn-label';
btn.appendChild(label);
// 保存 label 引用
if (type === 'hide') {
this.hideLabelEl = label;
} else if (type === 'reverse') {
this.reverseLabelEl = label;
}
// 点击事件
btn.addEventListener('click', onClick);
return btn;
}
/**
* 创建轴向按钮(仅文字)
*/
private createAxisButton(
_type: 'axisX' | 'axisY' | 'axisZ',
text: string,
onClick: () => void
): HTMLButtonElement {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'section-axis-btn section-axis-btn-text';
// 文字标签(既是图标也是标签)
const label = document.createElement('div');
label.className = 'section-axis-btn-label';
label.textContent = text;
btn.appendChild(label);
// 点击事件
btn.addEventListener('click', onClick);
return btn;
}
/**
* 处理隐藏按钮切换
*/
private handleHideToggle(): void {
this.isHidden = !this.isHidden;
this.updateHideButtonState();
if (this.options.onHideToggle) {
this.options.onHideToggle(this.isHidden);
}
}
/**
* 处理反向按钮点击
*/
private handleReverse(): void {
if (this.options.onReverse) {
this.options.onReverse();
}
}
/**
* 处理轴向切换
*/
private handleAxisChange(axis: SectionAxis): void {
if (this.activeAxis === axis) {
return; // 已经是激活状态,不重复触发
}
this.activeAxis = axis;
this.updateAxisButtonsState();
if (this.options.onAxisChange) {
this.options.onAxisChange(axis);
}
}
/**
* 更新隐藏按钮状态
*/
private updateHideButtonState(): void {
if (this.isHidden) {
this.hideBtn.classList.add('active');
} else {
this.hideBtn.classList.remove('active');
}
}
/**
* 更新轴向按钮状态
*/
private updateAxisButtonsState(): void {
this.axisXBtn.classList.toggle('active', this.activeAxis === 'x');
this.axisYBtn.classList.toggle('active', this.activeAxis === 'y');
this.axisZBtn.classList.toggle('active', this.activeAxis === 'z');
}
}