feat: 新增底部Dock测量面板与回调联动
This commit is contained in:
582
src/components/measure-dock-panel/index.ts
Normal file
582
src/components/measure-dock-panel/index.ts
Normal file
@@ -0,0 +1,582 @@
|
||||
import './index.css';
|
||||
import type { ThemeConfig } from '../../themes/types';
|
||||
import { IBimComponent } from '../../types/component';
|
||||
import { t } from '../../services/locale';
|
||||
import { MEASURE_TYPES, type ClearHeightDirection, type ClearHeightSelectType, type MeasureMode } from '../../types/measure';
|
||||
import { getIcon } from '../../utils/icon-manager';
|
||||
import type { MeasureConfig, MeasurePrecision, MeasureUnit } from '../measure-panel/types';
|
||||
|
||||
const PRIMARY_MODES: MeasureMode[] = ['distance', 'clearHeight', 'clearDistance', 'elevation'];
|
||||
const SECONDARY_MODES: MeasureMode[] = ['point', 'angle', 'area', 'slope'];
|
||||
const CONFIG_CACHE_KEY = 'bim-engine:measure:config';
|
||||
const DEFAULT_CONFIG: MeasureConfig = {
|
||||
unit: 'mm',
|
||||
precision: 2
|
||||
};
|
||||
|
||||
export interface MeasureDockPanelOptions {
|
||||
defaultMode?: MeasureMode;
|
||||
defaultExpanded?: boolean;
|
||||
defaultClearHeightDirection?: ClearHeightDirection;
|
||||
defaultClearHeightSelectType?: ClearHeightSelectType;
|
||||
onModeChange?: (mode: MeasureMode) => void;
|
||||
onClearAll?: () => void;
|
||||
onSettings?: () => void;
|
||||
onConfigSave?: (config: MeasureConfig) => void;
|
||||
onClearHeightDirectionChange?: (direction: ClearHeightDirection) => void;
|
||||
onClearHeightSelectTypeChange?: (selectType: ClearHeightSelectType) => void;
|
||||
}
|
||||
|
||||
export class MeasureDockPanel implements IBimComponent {
|
||||
public readonly element: HTMLElement;
|
||||
private readonly options: MeasureDockPanelOptions;
|
||||
|
||||
private readonly modeButtons: Map<MeasureMode, HTMLButtonElement> = new Map();
|
||||
private readonly clearBtn: HTMLButtonElement;
|
||||
private readonly expandBtn: HTMLButtonElement;
|
||||
private readonly settingsBtn: HTMLButtonElement;
|
||||
private readonly secondaryRow: HTMLElement;
|
||||
private readonly clearHeightOptions: HTMLElement;
|
||||
private readonly clearHeightDirectionLabel: HTMLElement;
|
||||
private readonly clearHeightSelectTypeLabel: HTMLElement;
|
||||
private readonly directionButtons: Map<ClearHeightDirection, HTMLButtonElement> = new Map();
|
||||
private readonly selectTypeButtons: Map<ClearHeightSelectType, HTMLButtonElement> = new Map();
|
||||
private readonly mainView: HTMLElement;
|
||||
private readonly settingsView: HTMLElement;
|
||||
private readonly settingsUnitLabel: HTMLElement;
|
||||
private readonly settingsPrecisionLabel: HTMLElement;
|
||||
private readonly settingsUnitSelect: HTMLSelectElement;
|
||||
private readonly settingsPrecisionSelect: HTMLSelectElement;
|
||||
private readonly settingsSaveBtn: HTMLButtonElement;
|
||||
private readonly settingsBackBtn: HTMLButtonElement;
|
||||
|
||||
private activeMode: MeasureMode;
|
||||
private isExpanded: boolean;
|
||||
private clearHeightDirection: ClearHeightDirection;
|
||||
private clearHeightSelectType: ClearHeightSelectType;
|
||||
private view: 'main' | 'settings' = 'main';
|
||||
private config: MeasureConfig;
|
||||
private lockedWidthPx: number | null = null;
|
||||
|
||||
constructor(options: MeasureDockPanelOptions = {}) {
|
||||
this.options = options;
|
||||
this.activeMode = options.defaultMode ?? 'distance';
|
||||
this.isExpanded = options.defaultExpanded ?? false;
|
||||
this.clearHeightDirection = options.defaultClearHeightDirection ?? 1;
|
||||
this.clearHeightSelectType = options.defaultClearHeightSelectType ?? 'point';
|
||||
this.config = this.loadConfigFromCache() ?? { ...DEFAULT_CONFIG };
|
||||
|
||||
const {
|
||||
root,
|
||||
clearBtn,
|
||||
expandBtn,
|
||||
settingsBtn,
|
||||
secondaryRow,
|
||||
clearHeightOptions,
|
||||
clearHeightDirectionLabel,
|
||||
clearHeightSelectTypeLabel,
|
||||
mainView,
|
||||
settingsView,
|
||||
settingsUnitLabel,
|
||||
settingsPrecisionLabel,
|
||||
settingsUnitSelect,
|
||||
settingsPrecisionSelect,
|
||||
settingsSaveBtn,
|
||||
settingsBackBtn
|
||||
} = this.createDom();
|
||||
this.element = root;
|
||||
this.clearBtn = clearBtn;
|
||||
this.expandBtn = expandBtn;
|
||||
this.settingsBtn = settingsBtn;
|
||||
this.secondaryRow = secondaryRow;
|
||||
this.clearHeightOptions = clearHeightOptions;
|
||||
this.clearHeightDirectionLabel = clearHeightDirectionLabel;
|
||||
this.clearHeightSelectTypeLabel = clearHeightSelectTypeLabel;
|
||||
this.mainView = mainView;
|
||||
this.settingsView = settingsView;
|
||||
this.settingsUnitLabel = settingsUnitLabel;
|
||||
this.settingsPrecisionLabel = settingsPrecisionLabel;
|
||||
this.settingsUnitSelect = settingsUnitSelect;
|
||||
this.settingsPrecisionSelect = settingsPrecisionSelect;
|
||||
this.settingsSaveBtn = settingsSaveBtn;
|
||||
this.settingsBackBtn = settingsBackBtn;
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
this.setLocales();
|
||||
this.syncSettingsFormFromConfig();
|
||||
this.applyExpandedState();
|
||||
this.applyClearHeightOptionsState();
|
||||
this.applyViewState();
|
||||
this.syncActiveMode(this.activeMode);
|
||||
}
|
||||
|
||||
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-text-tertiary', theme.textTertiary);
|
||||
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-danger', theme.danger);
|
||||
style.setProperty('--bim-component-bg-hover', theme.componentBgHover);
|
||||
}
|
||||
|
||||
public setLocales(): void {
|
||||
for (const [mode, btn] of this.modeButtons.entries()) {
|
||||
const text = t(`measure.modes.${mode}`);
|
||||
btn.dataset.tooltip = text;
|
||||
btn.setAttribute('aria-label', text);
|
||||
}
|
||||
|
||||
const clearText = t('measure.actions.clearAll');
|
||||
this.clearBtn.dataset.tooltip = clearText;
|
||||
this.clearBtn.setAttribute('aria-label', clearText);
|
||||
|
||||
const expandText = this.isExpanded ? t('measure.actions.collapse') : t('measure.actions.expand');
|
||||
delete this.expandBtn.dataset.tooltip;
|
||||
this.expandBtn.setAttribute('aria-label', expandText);
|
||||
|
||||
const settingsText = t('measure.actions.settings');
|
||||
this.settingsBtn.dataset.tooltip = settingsText;
|
||||
this.settingsBtn.setAttribute('aria-label', settingsText);
|
||||
|
||||
this.clearHeightDirectionLabel.textContent = t('measure.clearHeight.direction');
|
||||
this.clearHeightSelectTypeLabel.textContent = t('measure.clearHeight.selectType');
|
||||
|
||||
this.directionButtons.get(0)!.textContent = t('measure.clearHeight.directionDown');
|
||||
this.directionButtons.get(1)!.textContent = t('measure.clearHeight.directionUp');
|
||||
this.selectTypeButtons.get('point')!.textContent = t('measure.clearHeight.selectPoint');
|
||||
this.selectTypeButtons.get('element')!.textContent = t('measure.clearHeight.selectElement');
|
||||
|
||||
this.settingsUnitLabel.textContent = t('measure.settings.unit');
|
||||
this.settingsPrecisionLabel.textContent = t('measure.settings.precision');
|
||||
this.settingsSaveBtn.textContent = t('measure.settings.save');
|
||||
this.settingsBackBtn.textContent = t('measure.settings.cancel');
|
||||
}
|
||||
|
||||
public switchMode(mode: MeasureMode, triggerCallback: boolean = true): void {
|
||||
this.activeMode = mode;
|
||||
this.syncActiveMode(mode);
|
||||
this.applyClearHeightOptionsState();
|
||||
this.closeSettingsView();
|
||||
if (triggerCallback) {
|
||||
this.options.onModeChange?.(mode);
|
||||
}
|
||||
|
||||
if (mode === 'clearHeight') {
|
||||
this.options.onClearHeightDirectionChange?.(this.clearHeightDirection);
|
||||
this.options.onClearHeightSelectTypeChange?.(this.clearHeightSelectType);
|
||||
}
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.element.remove();
|
||||
}
|
||||
|
||||
public getConfig(): MeasureConfig {
|
||||
return { ...this.config };
|
||||
}
|
||||
|
||||
private createDom(): {
|
||||
root: HTMLElement;
|
||||
clearBtn: HTMLButtonElement;
|
||||
expandBtn: HTMLButtonElement;
|
||||
settingsBtn: HTMLButtonElement;
|
||||
secondaryRow: HTMLElement;
|
||||
clearHeightOptions: HTMLElement;
|
||||
clearHeightDirectionLabel: HTMLElement;
|
||||
clearHeightSelectTypeLabel: HTMLElement;
|
||||
mainView: HTMLElement;
|
||||
settingsView: HTMLElement;
|
||||
settingsUnitLabel: HTMLElement;
|
||||
settingsPrecisionLabel: HTMLElement;
|
||||
settingsUnitSelect: HTMLSelectElement;
|
||||
settingsPrecisionSelect: HTMLSelectElement;
|
||||
settingsSaveBtn: HTMLButtonElement;
|
||||
settingsBackBtn: HTMLButtonElement;
|
||||
} {
|
||||
const root = document.createElement('div');
|
||||
root.className = 'measure-dock-panel';
|
||||
|
||||
const mainView = document.createElement('div');
|
||||
mainView.className = 'measure-dock-panel-main';
|
||||
|
||||
const clearHeightOptions = document.createElement('div');
|
||||
clearHeightOptions.className = 'measure-dock-clearheight-options';
|
||||
|
||||
const directionGroup = document.createElement('div');
|
||||
directionGroup.className = 'measure-dock-clearheight-group';
|
||||
const clearHeightDirectionLabel = document.createElement('span');
|
||||
clearHeightDirectionLabel.className = 'measure-dock-clearheight-label';
|
||||
const directionButtons = document.createElement('div');
|
||||
directionButtons.className = 'measure-dock-clearheight-buttons';
|
||||
|
||||
const directionDown = this.createClearHeightOptionButton(() => {
|
||||
this.setClearHeightDirection(0);
|
||||
});
|
||||
const directionUp = this.createClearHeightOptionButton(() => {
|
||||
this.setClearHeightDirection(1);
|
||||
});
|
||||
this.directionButtons.set(0, directionDown);
|
||||
this.directionButtons.set(1, directionUp);
|
||||
directionButtons.appendChild(directionDown);
|
||||
directionButtons.appendChild(directionUp);
|
||||
directionGroup.appendChild(clearHeightDirectionLabel);
|
||||
directionGroup.appendChild(directionButtons);
|
||||
|
||||
const selectTypeGroup = document.createElement('div');
|
||||
selectTypeGroup.className = 'measure-dock-clearheight-group';
|
||||
const clearHeightSelectTypeLabel = document.createElement('span');
|
||||
clearHeightSelectTypeLabel.className = 'measure-dock-clearheight-label';
|
||||
const selectTypeButtons = document.createElement('div');
|
||||
selectTypeButtons.className = 'measure-dock-clearheight-buttons';
|
||||
|
||||
const selectPoint = this.createClearHeightOptionButton(() => {
|
||||
this.setClearHeightSelectType('point');
|
||||
});
|
||||
const selectElement = this.createClearHeightOptionButton(() => {
|
||||
this.setClearHeightSelectType('element');
|
||||
});
|
||||
this.selectTypeButtons.set('point', selectPoint);
|
||||
this.selectTypeButtons.set('element', selectElement);
|
||||
selectTypeButtons.appendChild(selectPoint);
|
||||
selectTypeButtons.appendChild(selectElement);
|
||||
selectTypeGroup.appendChild(clearHeightSelectTypeLabel);
|
||||
selectTypeGroup.appendChild(selectTypeButtons);
|
||||
|
||||
clearHeightOptions.appendChild(directionGroup);
|
||||
clearHeightOptions.appendChild(selectTypeGroup);
|
||||
|
||||
const top = document.createElement('div');
|
||||
top.className = 'measure-dock-panel-top';
|
||||
|
||||
const modeZone = document.createElement('div');
|
||||
modeZone.className = 'measure-dock-panel-mode-zone';
|
||||
|
||||
const primaryRow = document.createElement('div');
|
||||
primaryRow.className = 'measure-dock-panel-mode-row';
|
||||
PRIMARY_MODES.forEach((mode) => {
|
||||
primaryRow.appendChild(this.createModeButton(mode));
|
||||
});
|
||||
|
||||
const secondaryRow = document.createElement('div');
|
||||
secondaryRow.className = 'measure-dock-panel-mode-row measure-dock-panel-mode-row-secondary';
|
||||
SECONDARY_MODES.forEach((mode) => {
|
||||
secondaryRow.appendChild(this.createModeButton(mode));
|
||||
});
|
||||
|
||||
const clearBtn = this.createIconButton('measure-dock-panel-action-clear', getIcon('delete'));
|
||||
clearBtn.addEventListener('click', () => {
|
||||
this.options.onClearAll?.();
|
||||
});
|
||||
|
||||
const settingsBtn = this.createIconButton('measure-dock-panel-action-settings', getIcon('settings'));
|
||||
settingsBtn.addEventListener('click', () => {
|
||||
this.openSettingsView();
|
||||
this.options.onSettings?.();
|
||||
});
|
||||
|
||||
primaryRow.appendChild(clearBtn);
|
||||
secondaryRow.appendChild(settingsBtn);
|
||||
|
||||
modeZone.appendChild(primaryRow);
|
||||
modeZone.appendChild(secondaryRow);
|
||||
|
||||
const actionZone = document.createElement('div');
|
||||
actionZone.className = 'measure-dock-panel-actions';
|
||||
|
||||
const expandBtn = this.createIconButton('measure-dock-panel-action-expand', getIcon('expand'));
|
||||
expandBtn.addEventListener('click', () => {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
this.applyExpandedState();
|
||||
this.setLocales();
|
||||
});
|
||||
|
||||
actionZone.appendChild(expandBtn);
|
||||
|
||||
top.appendChild(modeZone);
|
||||
top.appendChild(actionZone);
|
||||
|
||||
mainView.appendChild(clearHeightOptions);
|
||||
mainView.appendChild(top);
|
||||
|
||||
const {
|
||||
settingsView,
|
||||
settingsUnitLabel,
|
||||
settingsPrecisionLabel,
|
||||
settingsUnitSelect,
|
||||
settingsPrecisionSelect,
|
||||
settingsSaveBtn,
|
||||
settingsBackBtn
|
||||
} = this.createSettingsView();
|
||||
|
||||
root.appendChild(mainView);
|
||||
root.appendChild(settingsView);
|
||||
|
||||
return {
|
||||
root,
|
||||
clearBtn,
|
||||
expandBtn,
|
||||
settingsBtn,
|
||||
secondaryRow,
|
||||
clearHeightOptions,
|
||||
clearHeightDirectionLabel,
|
||||
clearHeightSelectTypeLabel,
|
||||
mainView,
|
||||
settingsView,
|
||||
settingsUnitLabel,
|
||||
settingsPrecisionLabel,
|
||||
settingsUnitSelect,
|
||||
settingsPrecisionSelect,
|
||||
settingsSaveBtn,
|
||||
settingsBackBtn
|
||||
};
|
||||
}
|
||||
|
||||
private createSettingsView(): {
|
||||
settingsView: HTMLElement;
|
||||
settingsUnitLabel: HTMLElement;
|
||||
settingsPrecisionLabel: HTMLElement;
|
||||
settingsUnitSelect: HTMLSelectElement;
|
||||
settingsPrecisionSelect: HTMLSelectElement;
|
||||
settingsSaveBtn: HTMLButtonElement;
|
||||
settingsBackBtn: HTMLButtonElement;
|
||||
} {
|
||||
const settingsView = document.createElement('div');
|
||||
settingsView.className = 'measure-dock-panel-settings';
|
||||
|
||||
const unitRow = document.createElement('div');
|
||||
unitRow.className = 'measure-dock-settings-row';
|
||||
const settingsUnitLabel = document.createElement('span');
|
||||
settingsUnitLabel.className = 'measure-dock-settings-label';
|
||||
const settingsUnitSelect = document.createElement('select');
|
||||
settingsUnitSelect.className = 'measure-dock-settings-select';
|
||||
['m', 'cm', 'mm', 'km'].forEach((unit) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = unit;
|
||||
option.textContent = unit;
|
||||
settingsUnitSelect.appendChild(option);
|
||||
});
|
||||
unitRow.appendChild(settingsUnitLabel);
|
||||
unitRow.appendChild(settingsUnitSelect);
|
||||
|
||||
const precisionRow = document.createElement('div');
|
||||
precisionRow.className = 'measure-dock-settings-row';
|
||||
const settingsPrecisionLabel = document.createElement('span');
|
||||
settingsPrecisionLabel.className = 'measure-dock-settings-label';
|
||||
const settingsPrecisionSelect = document.createElement('select');
|
||||
settingsPrecisionSelect.className = 'measure-dock-settings-select';
|
||||
[0, 1, 2, 3].forEach((precision) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = String(precision);
|
||||
option.textContent = precision === 0 ? '0' : `0.${'0'.repeat(precision)}`;
|
||||
settingsPrecisionSelect.appendChild(option);
|
||||
});
|
||||
precisionRow.appendChild(settingsPrecisionLabel);
|
||||
precisionRow.appendChild(settingsPrecisionSelect);
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'measure-dock-settings-actions';
|
||||
const settingsSaveBtn = document.createElement('button');
|
||||
settingsSaveBtn.type = 'button';
|
||||
settingsSaveBtn.className = 'measure-dock-settings-btn is-save';
|
||||
settingsSaveBtn.addEventListener('click', () => {
|
||||
this.saveSettings();
|
||||
});
|
||||
const settingsBackBtn = document.createElement('button');
|
||||
settingsBackBtn.type = 'button';
|
||||
settingsBackBtn.className = 'measure-dock-settings-btn is-back';
|
||||
settingsBackBtn.addEventListener('click', () => {
|
||||
this.closeSettingsView();
|
||||
});
|
||||
actions.appendChild(settingsSaveBtn);
|
||||
actions.appendChild(settingsBackBtn);
|
||||
|
||||
settingsView.appendChild(unitRow);
|
||||
settingsView.appendChild(precisionRow);
|
||||
settingsView.appendChild(actions);
|
||||
|
||||
return {
|
||||
settingsView,
|
||||
settingsUnitLabel,
|
||||
settingsPrecisionLabel,
|
||||
settingsUnitSelect,
|
||||
settingsPrecisionSelect,
|
||||
settingsSaveBtn,
|
||||
settingsBackBtn
|
||||
};
|
||||
}
|
||||
|
||||
private createClearHeightOptionButton(onClick: () => void): HTMLButtonElement {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'measure-dock-clearheight-btn';
|
||||
button.addEventListener('click', onClick);
|
||||
return button;
|
||||
}
|
||||
|
||||
private createModeButton(mode: MeasureMode): HTMLButtonElement {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'measure-dock-panel-mode-btn';
|
||||
button.dataset.mode = mode;
|
||||
button.innerHTML = `<span class="measure-dock-panel-mode-icon">${MEASURE_TYPES[mode].icon}</span>`;
|
||||
button.addEventListener('click', () => {
|
||||
this.switchMode(mode);
|
||||
});
|
||||
this.modeButtons.set(mode, button);
|
||||
return button;
|
||||
}
|
||||
|
||||
private createIconButton(className: string, iconSvg: string): HTMLButtonElement {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = `measure-dock-panel-action-btn ${className}`;
|
||||
button.innerHTML = iconSvg;
|
||||
return button;
|
||||
}
|
||||
|
||||
private applyExpandedState(): void {
|
||||
this.secondaryRow.style.display = this.isExpanded ? 'grid' : 'none';
|
||||
this.expandBtn.classList.toggle('is-expanded', this.isExpanded);
|
||||
this.expandBtn.classList.toggle('is-collapsed', !this.isExpanded);
|
||||
}
|
||||
|
||||
private openSettingsView(): void {
|
||||
this.lockPanelWidth();
|
||||
this.view = 'settings';
|
||||
this.syncSettingsFormFromConfig();
|
||||
this.applyViewState();
|
||||
}
|
||||
|
||||
private closeSettingsView(): void {
|
||||
if (this.view !== 'settings') {
|
||||
return;
|
||||
}
|
||||
this.view = 'main';
|
||||
this.unlockPanelWidth();
|
||||
this.applyViewState();
|
||||
}
|
||||
|
||||
private saveSettings(): void {
|
||||
const nextUnit = this.settingsUnitSelect.value as MeasureUnit;
|
||||
const nextPrecision = Number(this.settingsPrecisionSelect.value) as MeasurePrecision;
|
||||
if (!this.isValidUnit(nextUnit) || !this.isValidPrecision(nextPrecision)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.config = {
|
||||
unit: nextUnit,
|
||||
precision: nextPrecision
|
||||
};
|
||||
this.saveConfigToCache(this.config);
|
||||
this.options.onConfigSave?.(this.getConfig());
|
||||
this.view = 'main';
|
||||
this.unlockPanelWidth();
|
||||
this.applyViewState();
|
||||
}
|
||||
|
||||
private lockPanelWidth(): void {
|
||||
const width = this.element.getBoundingClientRect().width;
|
||||
if (width <= 0) {
|
||||
return;
|
||||
}
|
||||
this.lockedWidthPx = Math.ceil(width);
|
||||
this.element.style.width = `${this.lockedWidthPx}px`;
|
||||
}
|
||||
|
||||
private unlockPanelWidth(): void {
|
||||
this.lockedWidthPx = null;
|
||||
this.element.style.removeProperty('width');
|
||||
}
|
||||
|
||||
private syncSettingsFormFromConfig(): void {
|
||||
this.settingsUnitSelect.value = this.config.unit;
|
||||
this.settingsPrecisionSelect.value = String(this.config.precision);
|
||||
}
|
||||
|
||||
private applyViewState(): void {
|
||||
const showMain = this.view === 'main';
|
||||
this.mainView.style.display = showMain ? 'block' : 'none';
|
||||
this.settingsView.style.display = showMain ? 'none' : 'flex';
|
||||
}
|
||||
|
||||
private applyClearHeightOptionsState(): void {
|
||||
// this.clearHeightOptions.classList.toggle('is-visible', this.activeMode === 'clearHeight');
|
||||
this.clearHeightOptions.classList.remove('is-visible');
|
||||
|
||||
for (const [direction, button] of this.directionButtons.entries()) {
|
||||
button.classList.toggle('is-active', direction === this.clearHeightDirection);
|
||||
}
|
||||
for (const [selectType, button] of this.selectTypeButtons.entries()) {
|
||||
button.classList.toggle('is-active', selectType === this.clearHeightSelectType);
|
||||
}
|
||||
}
|
||||
|
||||
private setClearHeightDirection(direction: ClearHeightDirection): void {
|
||||
if (this.clearHeightDirection === direction) {
|
||||
return;
|
||||
}
|
||||
this.clearHeightDirection = direction;
|
||||
this.applyClearHeightOptionsState();
|
||||
this.options.onClearHeightDirectionChange?.(direction);
|
||||
}
|
||||
|
||||
private setClearHeightSelectType(selectType: ClearHeightSelectType): void {
|
||||
if (this.clearHeightSelectType === selectType) {
|
||||
return;
|
||||
}
|
||||
this.clearHeightSelectType = selectType;
|
||||
this.applyClearHeightOptionsState();
|
||||
this.options.onClearHeightSelectTypeChange?.(selectType);
|
||||
}
|
||||
|
||||
private loadConfigFromCache(): MeasureConfig | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(CONFIG_CACHE_KEY);
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const parsed = JSON.parse(raw) as Partial<MeasureConfig>;
|
||||
if (!parsed || typeof parsed !== 'object') {
|
||||
return null;
|
||||
}
|
||||
if (!this.isValidUnit(parsed.unit) || !this.isValidPrecision(parsed.precision)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
unit: parsed.unit,
|
||||
precision: parsed.precision
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private saveConfigToCache(config: MeasureConfig): void {
|
||||
try {
|
||||
localStorage.setItem(CONFIG_CACHE_KEY, JSON.stringify(config));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private isValidUnit(unit: unknown): unit is MeasureUnit {
|
||||
return unit === 'm' || unit === 'cm' || unit === 'mm' || unit === 'km';
|
||||
}
|
||||
|
||||
private isValidPrecision(precision: unknown): precision is MeasurePrecision {
|
||||
return precision === 0 || precision === 1 || precision === 2 || precision === 3;
|
||||
}
|
||||
|
||||
private syncActiveMode(mode: MeasureMode): void {
|
||||
for (const [key, button] of this.modeButtons.entries()) {
|
||||
button.classList.toggle('is-active', key === mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user