feat: 迁移BimEngine到radial+dock并完善测量/剖切/漫游面板
This commit is contained in:
@@ -23,6 +23,12 @@
|
||||
box-shadow: var(--bd-shadow, 0 2px 8px rgba(15, 23, 42, 0.1));
|
||||
transition: transform 220ms ease, opacity 200ms ease;
|
||||
overflow: visible;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.bottom-dock-panel:hover,
|
||||
.bottom-dock-panel:focus-within {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.bottom-dock-panel.is-entering {
|
||||
|
||||
@@ -94,6 +94,7 @@ export interface EngineSettingPreset {
|
||||
settings: EngineSettings;
|
||||
readonly?: boolean;
|
||||
source?: 'sdk-default' | 'external' | 'user';
|
||||
allowModify?: boolean;
|
||||
}
|
||||
|
||||
export interface PresetListItem {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
background: color-mix(in srgb, var(--bim-bg-elevated, #e8ecf2) 92%, #ffffff 8%);
|
||||
box-sizing: border-box;
|
||||
color: var(--bim-text-secondary, #475569);
|
||||
font-size: 13px;
|
||||
@@ -187,7 +186,6 @@
|
||||
|
||||
.measure-dock-panel-mode-btn.is-active {
|
||||
border-color: color-mix(in srgb, var(--bim-primary, #4f88ff) 70%, #9db9ff 30%);
|
||||
background: color-mix(in srgb, var(--bim-primary-subtle, rgba(96, 140, 255, 0.18)) 72%, #ffffff 28%);
|
||||
color: color-mix(in srgb, var(--bim-primary, #4f88ff) 78%, #6f9dff 22%);
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--bim-primary, #4f88ff) 35%, transparent 65%);
|
||||
}
|
||||
@@ -332,4 +330,4 @@
|
||||
.measure-dock-panel-action-expand.is-expanded {
|
||||
height: 74px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,23 +400,21 @@ export class RadialToolbar implements IBimComponent {
|
||||
style.setProperty('--bim-icon-inverse', theme.iconInverse);
|
||||
style.setProperty('--bim-shadow-glow', theme.shadowGlow);
|
||||
|
||||
const isDark = theme.name === 'dark';
|
||||
style.setProperty('--rt-main-bg', theme.bgBase);
|
||||
style.setProperty('--rt-main-bg-hover', theme.bgBase);
|
||||
style.setProperty('--rt-main-border', theme.floatingBtnBorder);
|
||||
style.setProperty('--rt-main-shadow', theme.floatingBtnShadow);
|
||||
style.setProperty('--rt-main-shadow-hover', theme.floatingBtnShadowHover);
|
||||
style.setProperty('--rt-main-icon', theme.floatingIconColor);
|
||||
style.setProperty('--rt-main-icon-hover', theme.floatingIconColorHover);
|
||||
|
||||
style.setProperty('--rt-main-bg', isDark ? 'rgba(55, 68, 86, 0.92)' : theme.floatingBtnBg);
|
||||
style.setProperty('--rt-main-bg-hover', isDark ? 'rgba(66, 82, 104, 0.96)' : theme.floatingBtnBgHover);
|
||||
style.setProperty('--rt-main-border', isDark ? 'rgba(117, 133, 154, 0.56)' : theme.floatingBtnBorder);
|
||||
style.setProperty('--rt-main-shadow', isDark ? '0 2px 8px rgba(15, 23, 42, 0.32), 0 4px 12px rgba(15, 23, 42, 0.24)' : theme.floatingBtnShadow);
|
||||
style.setProperty('--rt-main-shadow-hover', isDark ? '0 4px 12px rgba(15, 23, 42, 0.38), 0 6px 20px rgba(15, 23, 42, 0.3)' : theme.floatingBtnShadowHover);
|
||||
style.setProperty('--rt-main-icon', isDark ? '#e2e8f0' : theme.floatingIconColor);
|
||||
style.setProperty('--rt-main-icon-hover', isDark ? '#f8fafc' : theme.floatingIconColorHover);
|
||||
|
||||
style.setProperty('--rt-sub-bg', isDark ? 'rgba(55, 68, 86, 0.92)' : theme.floatingBtnBg);
|
||||
style.setProperty('--rt-sub-bg-hover', isDark ? 'rgba(66, 82, 104, 0.96)' : theme.floatingBtnBgHover);
|
||||
style.setProperty('--rt-sub-border', isDark ? 'rgba(117, 133, 154, 0.56)' : theme.floatingBtnBorder);
|
||||
style.setProperty('--rt-sub-shadow', isDark ? '0 2px 8px rgba(15, 23, 42, 0.32), 0 4px 12px rgba(15, 23, 42, 0.24)' : theme.floatingBtnShadow);
|
||||
style.setProperty('--rt-sub-shadow-hover', isDark ? '0 4px 12px rgba(15, 23, 42, 0.38), 0 6px 20px rgba(15, 23, 42, 0.3)' : theme.floatingBtnShadowHover);
|
||||
style.setProperty('--rt-sub-icon', isDark ? '#e2e8f0' : theme.floatingIconColor);
|
||||
style.setProperty('--rt-sub-icon-hover', isDark ? '#f8fafc' : theme.floatingIconColorHover);
|
||||
style.setProperty('--rt-sub-bg', theme.bgBase);
|
||||
style.setProperty('--rt-sub-bg-hover', theme.primaryHover);
|
||||
style.setProperty('--rt-sub-border', theme.floatingBtnBorder);
|
||||
style.setProperty('--rt-sub-shadow', theme.floatingBtnShadow);
|
||||
style.setProperty('--rt-sub-shadow-hover', theme.floatingBtnShadowHover);
|
||||
style.setProperty('--rt-sub-icon', theme.floatingIconColor);
|
||||
style.setProperty('--rt-sub-icon-hover', theme.iconHover);
|
||||
}
|
||||
|
||||
public setItemActive(id: string, active: boolean): void {
|
||||
|
||||
142
src/components/section-dock-panel/index.css
Normal file
142
src/components/section-dock-panel/index.css
Normal file
@@ -0,0 +1,142 @@
|
||||
.section-dock-panel {
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
color: var(--bim-text-secondary, #475569);
|
||||
}
|
||||
|
||||
.section-dock-axis-panel {
|
||||
display: none;
|
||||
width: fit-content;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-dock-axis-panel.is-visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.section-dock-axis-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
background: color-mix(in srgb, var(--bim-bg-inset, #edf1f6) 92%, #ffffff 8%);
|
||||
color: color-mix(in srgb, var(--bim-text-secondary, #64748b) 94%, #475569 6%);
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.section-dock-axis-btn:hover {
|
||||
border-color: rgba(148, 163, 184, 0.5);
|
||||
background: color-mix(in srgb, var(--bim-component-bg-hover, #dce5f2) 64%, #ffffff 36%);
|
||||
}
|
||||
|
||||
.section-dock-axis-btn.is-active {
|
||||
border-color: color-mix(in srgb, var(--bim-primary, #4f88ff) 70%, #9db9ff 30%);
|
||||
background: color-mix(in srgb, var(--bim-primary-subtle, rgba(96, 140, 255, 0.18)) 72%, #ffffff 28%);
|
||||
color: color-mix(in srgb, var(--bim-primary, #4f88ff) 78%, #6f9dff 22%);
|
||||
}
|
||||
|
||||
.section-dock-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.section-dock-types,
|
||||
.section-dock-tools {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-dock-divider {
|
||||
width: 1px;
|
||||
height: 32px;
|
||||
background: color-mix(in srgb, var(--bim-border-default, #cbd5e1) 84%, transparent 16%);
|
||||
}
|
||||
|
||||
.section-dock-type-btn,
|
||||
.section-dock-tool-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--bim-bg-inset, #edf1f6) 92%, #ffffff 8%);
|
||||
color: color-mix(in srgb, var(--bim-text-secondary, #64748b) 94%, #475569 6%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.section-dock-type-btn:hover,
|
||||
.section-dock-tool-btn:hover {
|
||||
border-color: var(--bim-border-strong, rgba(100, 116, 139, 0.6));
|
||||
background: color-mix(in srgb, var(--bim-component-bg-hover, #dce5f2) 64%, #ffffff 36%);
|
||||
}
|
||||
|
||||
.section-dock-type-btn.is-active,
|
||||
.section-dock-tool-btn.is-active {
|
||||
border-color: color-mix(in srgb, var(--bim-primary, #4f88ff) 70%, #9db9ff 30%);
|
||||
color: color-mix(in srgb, var(--bim-primary, #4f88ff) 78%, #6f9dff 22%);
|
||||
}
|
||||
|
||||
.section-dock-type-icon,
|
||||
.section-dock-tool-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.section-dock-type-icon svg,
|
||||
.section-dock-tool-icon svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.section-dock-panel [data-tooltip] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section-dock-panel [data-tooltip]::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: calc(100% + 6px);
|
||||
transform: translateX(-50%);
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
background: rgba(15, 23, 42, 0.92);
|
||||
color: #f8fafc;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
z-index: 8;
|
||||
transition: opacity 60ms ease;
|
||||
}
|
||||
|
||||
.section-dock-panel [data-tooltip]:hover::after,
|
||||
.section-dock-panel [data-tooltip]:focus-visible::after {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
330
src/components/section-dock-panel/index.ts
Normal file
330
src/components/section-dock-panel/index.ts
Normal file
@@ -0,0 +1,330 @@
|
||||
import './index.css';
|
||||
import type { ThemeConfig } from '../../themes/types';
|
||||
import { IBimComponent } from '../../types/component';
|
||||
import { t } from '../../services/locale';
|
||||
import { getIcon } from '../../utils/icon-manager';
|
||||
|
||||
export type SectionDockType = 'face' | 'axis' | 'box';
|
||||
export type SectionDockAxis = 'x' | 'y' | 'z';
|
||||
|
||||
export interface SectionDockPanelOptions {
|
||||
defaultType?: SectionDockType;
|
||||
defaultAxis?: SectionDockAxis;
|
||||
defaultHidden?: boolean;
|
||||
onTypeChange?: (type: SectionDockType, axis: SectionDockAxis) => void;
|
||||
onAxisChange?: (axis: SectionDockAxis) => void;
|
||||
onHideToggle?: (hidden: boolean) => void;
|
||||
onReverse?: () => void;
|
||||
onReset?: (type: SectionDockType, axis: SectionDockAxis) => void;
|
||||
onFitToModel?: () => void;
|
||||
}
|
||||
|
||||
interface ToolDefinition {
|
||||
key: 'hide' | 'reverse' | 'reset' | 'fit';
|
||||
iconName: string;
|
||||
textKey: string;
|
||||
onClick: () => void;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export class SectionDockPanel implements IBimComponent {
|
||||
public readonly element: HTMLElement;
|
||||
private readonly options: SectionDockPanelOptions;
|
||||
|
||||
private readonly typeButtons: Map<SectionDockType, HTMLButtonElement> = new Map();
|
||||
private readonly axisButtons: Map<SectionDockAxis, HTMLButtonElement> = new Map();
|
||||
private readonly toolContainer: HTMLElement;
|
||||
private readonly axisPanel: HTMLElement;
|
||||
|
||||
private activeType: SectionDockType;
|
||||
private activeAxis: SectionDockAxis;
|
||||
private isHidden: boolean;
|
||||
|
||||
constructor(options: SectionDockPanelOptions = {}) {
|
||||
this.options = options;
|
||||
this.activeType = options.defaultType ?? 'face';
|
||||
this.activeAxis = options.defaultAxis ?? 'x';
|
||||
this.isHidden = options.defaultHidden ?? false;
|
||||
|
||||
const { root, toolContainer, axisPanel } = this.createDom();
|
||||
this.element = root;
|
||||
this.toolContainer = toolContainer;
|
||||
this.axisPanel = axisPanel;
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
this.applyTypeState();
|
||||
this.applyAxisState();
|
||||
this.renderTools();
|
||||
this.setLocales();
|
||||
}
|
||||
|
||||
public setTheme(theme: ThemeConfig): void {
|
||||
const style = this.element.style;
|
||||
style.setProperty('--bim-text-primary', theme.textPrimary);
|
||||
style.setProperty('--bim-text-secondary', theme.textSecondary);
|
||||
style.setProperty('--bim-border-default', theme.borderDefault);
|
||||
style.setProperty('--bim-border-strong', theme.borderStrong);
|
||||
style.setProperty('--bim-bg-inset', theme.bgInset);
|
||||
style.setProperty('--bim-bg-elevated', theme.bgElevated);
|
||||
style.setProperty('--bim-primary', theme.primary);
|
||||
style.setProperty('--bim-primary-subtle', theme.primarySubtle);
|
||||
style.setProperty('--bim-component-bg-hover', theme.componentBgHover);
|
||||
}
|
||||
|
||||
public setLocales(): void {
|
||||
this.typeButtons.get('face')!.dataset.tooltip = t('toolbar.sectionPlane');
|
||||
this.typeButtons.get('axis')!.dataset.tooltip = t('toolbar.sectionAxis');
|
||||
this.typeButtons.get('box')!.dataset.tooltip = t('toolbar.sectionBox');
|
||||
|
||||
this.axisButtons.get('x')!.dataset.label = 'X';
|
||||
this.axisButtons.get('y')!.dataset.label = 'Y';
|
||||
this.axisButtons.get('z')!.dataset.label = 'Z';
|
||||
|
||||
this.typeButtons.forEach((button) => {
|
||||
button.setAttribute('aria-label', button.dataset.tooltip ?? '');
|
||||
});
|
||||
this.axisButtons.forEach((button) => {
|
||||
button.setAttribute('aria-label', button.dataset.label ?? '');
|
||||
});
|
||||
|
||||
this.renderTools();
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.element.remove();
|
||||
}
|
||||
|
||||
public resetForOpen(): void {
|
||||
this.activeType = 'face';
|
||||
this.isHidden = false;
|
||||
this.applyTypeState();
|
||||
this.renderTools();
|
||||
this.options.onTypeChange?.(this.activeType, this.activeAxis);
|
||||
this.options.onHideToggle?.(false);
|
||||
}
|
||||
|
||||
public getActiveType(): SectionDockType {
|
||||
return this.activeType;
|
||||
}
|
||||
|
||||
public getActiveAxis(): SectionDockAxis {
|
||||
return this.activeAxis;
|
||||
}
|
||||
|
||||
private createDom(): { root: HTMLElement; toolContainer: HTMLElement; axisPanel: HTMLElement } {
|
||||
const root = document.createElement('div');
|
||||
root.className = 'section-dock-panel';
|
||||
|
||||
const axisPanel = document.createElement('div');
|
||||
axisPanel.className = 'section-dock-axis-panel';
|
||||
|
||||
(['x', 'y', 'z'] as SectionDockAxis[]).forEach((axis) => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'section-dock-axis-btn';
|
||||
button.textContent = axis.toUpperCase();
|
||||
button.addEventListener('click', () => {
|
||||
this.handleAxisChange(axis);
|
||||
});
|
||||
this.axisButtons.set(axis, button);
|
||||
axisPanel.appendChild(button);
|
||||
});
|
||||
|
||||
const main = document.createElement('div');
|
||||
main.className = 'section-dock-main';
|
||||
|
||||
const left = document.createElement('div');
|
||||
left.className = 'section-dock-types';
|
||||
|
||||
const faceBtn = this.createTypeButton('face', getIcon('拾曲面剖切'));
|
||||
const axisBtn = this.createTypeButton('axis', getIcon('轴向剖切'));
|
||||
const boxBtn = this.createTypeButton('box', getIcon('剖切盒'));
|
||||
|
||||
left.appendChild(faceBtn);
|
||||
left.appendChild(axisBtn);
|
||||
left.appendChild(boxBtn);
|
||||
|
||||
const divider = document.createElement('div');
|
||||
divider.className = 'section-dock-divider';
|
||||
|
||||
const toolContainer = document.createElement('div');
|
||||
toolContainer.className = 'section-dock-tools';
|
||||
|
||||
main.appendChild(left);
|
||||
main.appendChild(divider);
|
||||
main.appendChild(toolContainer);
|
||||
|
||||
root.appendChild(axisPanel);
|
||||
root.appendChild(main);
|
||||
|
||||
return { root, toolContainer, axisPanel };
|
||||
}
|
||||
|
||||
private createTypeButton(type: SectionDockType, iconSvg: string): HTMLButtonElement {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'section-dock-type-btn';
|
||||
|
||||
const icon = document.createElement('span');
|
||||
icon.className = 'section-dock-type-icon';
|
||||
icon.innerHTML = iconSvg;
|
||||
|
||||
button.appendChild(icon);
|
||||
button.addEventListener('click', () => {
|
||||
this.handleTypeChange(type);
|
||||
});
|
||||
|
||||
this.typeButtons.set(type, button);
|
||||
return button;
|
||||
}
|
||||
|
||||
private handleTypeChange(type: SectionDockType): void {
|
||||
if (this.activeType === type) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeType = type;
|
||||
this.applyTypeState();
|
||||
this.renderTools();
|
||||
|
||||
if (this.isHidden) {
|
||||
this.isHidden = false;
|
||||
this.options.onHideToggle?.(false);
|
||||
}
|
||||
|
||||
this.options.onTypeChange?.(type, this.activeAxis);
|
||||
}
|
||||
|
||||
private handleAxisChange(axis: SectionDockAxis): void {
|
||||
if (this.activeAxis === axis) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeAxis = axis;
|
||||
this.applyAxisState();
|
||||
|
||||
if (this.activeType === 'axis') {
|
||||
this.options.onAxisChange?.(axis);
|
||||
}
|
||||
}
|
||||
|
||||
private applyTypeState(): void {
|
||||
this.typeButtons.forEach((button, type) => {
|
||||
button.classList.toggle('is-active', type === this.activeType);
|
||||
});
|
||||
|
||||
const axisVisible = this.activeType === 'axis';
|
||||
this.axisPanel.classList.toggle('is-visible', axisVisible);
|
||||
}
|
||||
|
||||
private applyAxisState(): void {
|
||||
this.axisButtons.forEach((button, axis) => {
|
||||
button.classList.toggle('is-active', axis === this.activeAxis);
|
||||
});
|
||||
}
|
||||
|
||||
private renderTools(): void {
|
||||
this.toolContainer.innerHTML = '';
|
||||
|
||||
this.getToolsForType(this.activeType).forEach((tool) => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'section-dock-tool-btn';
|
||||
if (tool.isActive) {
|
||||
button.classList.add('is-active');
|
||||
}
|
||||
|
||||
const icon = document.createElement('span');
|
||||
icon.className = 'section-dock-tool-icon';
|
||||
icon.innerHTML = getIcon(tool.iconName);
|
||||
|
||||
button.appendChild(icon);
|
||||
const text = t(tool.textKey);
|
||||
button.dataset.tooltip = text;
|
||||
button.setAttribute('aria-label', text);
|
||||
button.addEventListener('click', tool.onClick);
|
||||
|
||||
this.toolContainer.appendChild(button);
|
||||
});
|
||||
}
|
||||
|
||||
private getToolsForType(type: SectionDockType): ToolDefinition[] {
|
||||
const hideTool: ToolDefinition = {
|
||||
key: 'hide',
|
||||
iconName: '隐藏',
|
||||
textKey: type === 'box' ? 'sectionBox.actions.hide' : type === 'axis' ? 'sectionAxis.actions.hide' : 'sectionPlane.actions.hide',
|
||||
isActive: this.isHidden,
|
||||
onClick: () => {
|
||||
this.isHidden = !this.isHidden;
|
||||
this.options.onHideToggle?.(this.isHidden);
|
||||
this.renderTools();
|
||||
}
|
||||
};
|
||||
|
||||
const reverseTool: ToolDefinition = {
|
||||
key: 'reverse',
|
||||
iconName: '反向',
|
||||
textKey: type === 'axis' ? 'sectionAxis.actions.reverse' : type === 'box' ? 'sectionBox.actions.reverse' : 'sectionPlane.actions.reverse',
|
||||
onClick: () => {
|
||||
this.options.onReverse?.();
|
||||
}
|
||||
};
|
||||
|
||||
if (type === 'axis') {
|
||||
return [
|
||||
hideTool,
|
||||
reverseTool,
|
||||
{
|
||||
key: 'reset',
|
||||
iconName: '重置',
|
||||
textKey: 'sectionAxis.actions.reset',
|
||||
onClick: () => {
|
||||
this.isHidden = false;
|
||||
this.options.onReset?.(this.activeType, this.activeAxis);
|
||||
this.renderTools();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (type === 'box') {
|
||||
return [
|
||||
hideTool,
|
||||
{
|
||||
key: 'fit',
|
||||
iconName: '适应到模型',
|
||||
textKey: 'sectionBox.actions.fitToModel',
|
||||
onClick: () => {
|
||||
this.options.onFitToModel?.();
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'reset',
|
||||
iconName: '重置',
|
||||
textKey: 'sectionBox.actions.reset',
|
||||
onClick: () => {
|
||||
this.isHidden = false;
|
||||
this.options.onReset?.(this.activeType, this.activeAxis);
|
||||
this.renderTools();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
hideTool,
|
||||
reverseTool,
|
||||
{
|
||||
key: 'reset',
|
||||
iconName: '重置',
|
||||
textKey: 'sectionPlane.actions.reset',
|
||||
onClick: () => {
|
||||
this.isHidden = false;
|
||||
this.options.onReset?.(this.activeType, this.activeAxis);
|
||||
this.renderTools();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
107
src/components/walk-dock-panel/index.css
Normal file
107
src/components/walk-dock-panel/index.css
Normal file
@@ -0,0 +1,107 @@
|
||||
.walk-control-panel.walk-dock-panel {
|
||||
gap: 10px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
box-shadow: none;
|
||||
color: var(--bim-text-secondary, #64748b);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-divider {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-control-left,
|
||||
.walk-control-panel.walk-dock-panel .walk-control-settings {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-icon-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
background: color-mix(in srgb, var(--bim-bg-inset, #edf1f6) 92%, #ffffff 8%);
|
||||
color: color-mix(in srgb, var(--bim-text-secondary, #64748b) 94%, #475569 6%);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-icon-btn:hover {
|
||||
border-color: rgba(148, 163, 184, 0.5);
|
||||
background: color-mix(in srgb, var(--bim-component-bg-hover, #dce5f2) 64%, #ffffff 36%);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-icon-btn.active {
|
||||
border-color: color-mix(in srgb, var(--bim-primary, #4f88ff) 70%, #9db9ff 30%);
|
||||
background: color-mix(in srgb, var(--bim-primary-subtle, rgba(96, 140, 255, 0.18)) 72%, #ffffff 28%);
|
||||
color: color-mix(in srgb, var(--bim-primary, #4f88ff) 78%, #6f9dff 22%);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-icon-btn svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-icon-btn.active svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-speed-control {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-speed-label,
|
||||
.walk-control-panel.walk-dock-panel .walk-checkbox-label,
|
||||
.walk-control-panel.walk-dock-panel .walk-select-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-speed-group {
|
||||
padding: 2px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
background: color-mix(in srgb, var(--bim-bg-inset, #edf1f6) 92%, #ffffff 8%);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-speed-btn {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
background: color-mix(in srgb, var(--bim-bg-elevated, #f8fafc) 88%, #ffffff 12%);
|
||||
color: var(--bim-text-primary, #0f172a);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-speed-display {
|
||||
min-width: 30px;
|
||||
font-size: 12px;
|
||||
color: var(--bim-text-primary, #0f172a);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-checkbox {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
accent-color: var(--bim-primary, #3b82f6);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-select {
|
||||
min-width: 90px;
|
||||
height: 24px;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
background: color-mix(in srgb, var(--bim-bg-inset, #edf1f6) 92%, #ffffff 8%);
|
||||
color: var(--bim-text-primary, #0f172a);
|
||||
}
|
||||
|
||||
.walk-control-panel.walk-dock-panel .walk-exit-btn {
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
39
src/components/walk-dock-panel/index.ts
Normal file
39
src/components/walk-dock-panel/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import './index.css';
|
||||
import { IBimComponent } from '../../types/component';
|
||||
import { WalkControlPanel } from '../walk-control-panel';
|
||||
import type { WalkControlPanelOptions, WalkControlState } from '../walk-control-panel/types';
|
||||
import type { ThemeConfig } from '../../themes/types';
|
||||
|
||||
export class WalkDockPanel implements IBimComponent {
|
||||
public readonly element: HTMLElement;
|
||||
private readonly panel: WalkControlPanel;
|
||||
|
||||
constructor(options: WalkControlPanelOptions = {}) {
|
||||
this.panel = new WalkControlPanel(options);
|
||||
this.panel.init();
|
||||
this.element = this.panel.element;
|
||||
this.element.classList.add('walk-dock-panel');
|
||||
}
|
||||
|
||||
public init(): void {}
|
||||
|
||||
public setPlanViewActive(active: boolean): void {
|
||||
this.panel.setPlanViewActive(active);
|
||||
}
|
||||
|
||||
public setLocales(): void {
|
||||
this.panel.setLocales();
|
||||
}
|
||||
|
||||
public setTheme(theme: ThemeConfig): void {
|
||||
this.panel.setTheme(theme);
|
||||
}
|
||||
|
||||
public getState(): WalkControlState {
|
||||
return this.panel.getState();
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.panel.destroy();
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
*/
|
||||
private render(): void {
|
||||
this.element.innerHTML = '';
|
||||
|
||||
|
||||
// 渲染路径设置区域
|
||||
const settings = this.createSettingsSection();
|
||||
this.element.appendChild(settings);
|
||||
@@ -104,13 +104,13 @@ export class WalkPathPanel implements IBimComponent {
|
||||
// ===== 漫游时间 =====
|
||||
const durationGroup = document.createElement('div');
|
||||
durationGroup.className = 'walk-path-form-group';
|
||||
|
||||
|
||||
const durationLabel = document.createElement('label');
|
||||
durationLabel.textContent = t('walkControl.path.duration');
|
||||
|
||||
|
||||
const durationWrapper = document.createElement('div');
|
||||
durationWrapper.className = 'walk-path-input-wrapper';
|
||||
|
||||
|
||||
const durationInput = document.createElement('input');
|
||||
durationInput.type = 'number';
|
||||
durationInput.className = 'walk-path-input';
|
||||
@@ -121,11 +121,11 @@ export class WalkPathPanel implements IBimComponent {
|
||||
const val = parseInt((e.target as HTMLInputElement).value) || 1;
|
||||
this.duration = val * 1000;
|
||||
};
|
||||
|
||||
|
||||
const durationUnit = document.createElement('span');
|
||||
durationUnit.className = 'walk-path-unit';
|
||||
durationUnit.textContent = t('walkControl.path.durationUnit');
|
||||
|
||||
|
||||
durationWrapper.appendChild(durationInput);
|
||||
durationWrapper.appendChild(durationUnit);
|
||||
durationGroup.appendChild(durationLabel);
|
||||
@@ -134,7 +134,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
// ===== 循环播放 =====
|
||||
const loopGroup = document.createElement('div');
|
||||
loopGroup.className = 'walk-path-form-group walk-path-form-group-inline';
|
||||
|
||||
|
||||
const loopCheckbox = document.createElement('input');
|
||||
loopCheckbox.type = 'checkbox';
|
||||
loopCheckbox.id = 'walk-path-loop-checkbox';
|
||||
@@ -144,11 +144,11 @@ export class WalkPathPanel implements IBimComponent {
|
||||
// 更新循环播放状态
|
||||
this.loop = (e.target as HTMLInputElement).checked;
|
||||
};
|
||||
|
||||
|
||||
const loopLabel = document.createElement('label');
|
||||
loopLabel.htmlFor = 'walk-path-loop-checkbox';
|
||||
loopLabel.textContent = t('walkControl.path.loop');
|
||||
|
||||
|
||||
loopGroup.appendChild(loopCheckbox);
|
||||
loopGroup.appendChild(loopLabel);
|
||||
|
||||
@@ -390,7 +390,7 @@ export class WalkPathPanel implements IBimComponent {
|
||||
if (!this.element) return;
|
||||
// 设置 CSS 变量
|
||||
this.element.style.setProperty('--bim-text-primary', theme.textPrimary ?? '#fff');
|
||||
this.element.style.setProperty('--bim-text-secondary', theme.textSecondary ?? '#94a3b8');
|
||||
this.element.style.setProperty('--bim-text-secondary', theme.textPrimary ?? '#fff');
|
||||
this.element.style.setProperty('--bim-bg-elevated', theme.bgElevated ?? '#1f2d3e');
|
||||
this.element.style.setProperty('--bim-border-default', theme.borderDefault ?? '#334155');
|
||||
this.element.style.setProperty('--bim-primary', theme.primary ?? '#3b82f6');
|
||||
|
||||
Reference in New Issue
Block a user