301 lines
9.2 KiB
TypeScript
301 lines
9.2 KiB
TypeScript
|
|
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');
|
|||
|
|
}
|
|||
|
|
}
|