refactor(settings): Encapsulate button logic in SettingsControls class

This commit is contained in:
Grol Grol 2025-04-27 07:57:53 +03:00
parent 6014663ac0
commit ae8740b3f9
2 changed files with 153 additions and 47 deletions

View file

@ -14,17 +14,17 @@ import { UnknownPolicySettingComponent } from "./components/UnknownPolicySetting
import { ConfirmModal, InfoModal, SaveConfirmModal } from "./modals";
import { SettingsValidator } from "./validation/SettingsValidator";
import { ValidationError } from "./validation/types";
import { ISettingsControlActions, SettingsControls } from "./SettingsControls";
export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
plugin: CheckboxSyncPlugin;
private settingGroups: SettingGroup[] = []
private applyButton: ButtonComponent;
private resetToDefaultButton: ButtonComponent;
private errorDisplay: ErrorDisplay;
private settingsControls: SettingsControls;
private isDirty: boolean = false;
private actionMutex = new Mutex();
@ -79,12 +79,12 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
private settingChanged() {
this.isDirty = true;
this.applyButton.setDisabled(false).setCta();
this.settingsControls.setApplyState({ disabled: false, cta: true });
}
private settingSaved() {
this.isDirty = false;
this.applyButton.setDisabled(true).removeCta();
this.settingsControls.setApplyState({ disabled: true, cta: false });
}
display(): void {
@ -100,47 +100,33 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
// --- Область для вывода ошибок ---
this.errorDisplay = new ErrorDisplay(containerEl);
// Используем Setting для группировки кнопок
const buttonGroup = new Setting(containerEl)
.setClass('checkbox-sync-button-group'); // Добавим класс для возможной стилизации
// Создаем экземпляр SettingsControls
const controlActions: ISettingsControlActions = {
onApply: () => this.applyChanges(),
onReset: () => this.resetInputsToSavedSettings(),
// Оставляем ConfirmModal здесь, но передаем вызов applyDefaultSettings
onResetDefaults: () => {
new ConfirmModal(this.app,
"Reset all settings to default and apply immediately?\nThis cannot be undone.",
// Только если пользователь нажал ОК, вызываем реальный сброс
async () => { await this.applyDefaultSettings(); }
// Optional: callback for cancel if needed
).open();
}
};
this.settingsControls = new SettingsControls(containerEl, controlActions);
// Устанавливаем начальное состояние кнопки Apply
// (isDirty здесь всегда false при первом вызове display)
this.settingsControls.setApplyState({ disabled: !this.isDirty, cta: this.isDirty });
buttonGroup.addButton(button => {
button
.setButtonText("Reset changes")
.setTooltip("Revert changes to the last applied settings")
.onClick(() => {
this.resetInputsToSavedSettings();
});
});
buttonGroup.addButton(button => {
this.resetToDefaultButton = button;
button
.setButtonText("Reset to defaults")
.setTooltip("Reset all settings to default values and apply immediately")
.onClick(() => {
new ConfirmModal(this.app,
"Reset all settings to default and apply immediately?\nThis cannot be undone.", // Добавил \n для переноса
async () => { await this.applyDefaultSettings(); }
).open();
});
});
// --- Кнопка Применить ---
buttonGroup
.addButton(button => {
this.applyButton = button;
button
.setButtonText("Apply Changes")
.setDisabled(!this.isDirty)
.onClick(async () => await this.applyChanges())
});
// Начальное состояние кнопки Reset Defaults (всегда включена)
this.settingsControls.setResetDefaultsState({ disabled: false });
}
private async applyChanges() {
await this.actionMutex.runExclusive(async () => {
const originalButtonText = "Apply Changes";
this.applyButton.setDisabled(true).setButtonText("Applying...").removeCta();
this.settingsControls.setApplyState({ disabled: true, text: 'Applying...', cta: false });
this.errorDisplay.clear();
try {
@ -156,9 +142,8 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
console.error("Unexpected error during applyChanges:", error);
const message = error.message || "Unknown error";
this.errorDisplay.displayMessage(`An unexpected error occurred: ${message}`);
this.applyButton.setDisabled(false).setCta();
} finally {
this.applyButton.setButtonText(originalButtonText); // Восстанавливаем текст
this.settingsControls.setApplyState({ disabled: false, cta: true })
}
});
}
@ -192,9 +177,8 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
*/
private async applyDefaultSettings() {
await this.actionMutex.runExclusive(async () => {
this.resetToDefaultButton.setDisabled(true).setButtonText("Resetting...");
this.errorDisplay.clear();// Очищаем предыдущие ошибки
this.settingsControls.setResetDefaultsState({ disabled: true, text: 'Resetting...' });
this.errorDisplay.clear();
try {
const defaultsCopy = { ...DEFAULT_SETTINGS }; // Работаем с копией
@ -212,10 +196,10 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
} catch (error: any) {
console.error("Error resetting settings to default:", error);
const message = error.message || "Unknown error";
this.errorDisplay.displayMessage(`Error resetting to defaults: ${message}`);
this.errorDisplay.displayMessage(`Error resetting to defaults: ${message}`);
} finally {
// Восстанавливаем кнопку Reset
this.resetToDefaultButton.setDisabled(false).setButtonText("Reset to defaults");
this.settingsControls.setResetDefaultsState({ disabled: false });
}
});
}

122
src/ui/SettingsControls.ts Normal file
View file

@ -0,0 +1,122 @@
import { ButtonComponent, Setting } from 'obsidian';
/**
* Интерфейс для действий, которые должны выполнять кнопки управления.
*/
export interface ISettingsControlActions {
onApply: () => Promise<void> | void;
onReset: () => Promise<void> | void;
onResetDefaults: () => Promise<void> | void;
}
/**
* Параметры для обновления состояния кнопки Apply.
*/
export interface ApplyButtonStateOptions {
disabled: boolean;
cta?: boolean; // Является ли кнопка основной (call to action)
text?: string; // Опциональный текст кнопки
}
/**
* Параметры для обновления состояния кнопки Reset Defaults.
*/
export interface ResetDefaultsButtonStateOptions {
disabled: boolean;
text?: string; // Опциональный текст кнопки
}
/**
* Отвечает за создание и управление кнопками
* "Apply Changes", "Reset changes", "Reset to defaults" на вкладке настроек.
*/
export class SettingsControls {
private applyButton: ButtonComponent;
private resetButton: ButtonComponent; // Кнопка "Reset changes"
private resetDefaultsButton: ButtonComponent;
private readonly defaultApplyText = "Apply Changes";
private readonly defaultResetDefaultsText = "Reset to defaults";
constructor(container: HTMLElement, private actions: ISettingsControlActions) {
const buttonGroup = new Setting(container)
.setClass('checkbox-sync-button-group');
// Кнопка "Reset changes"
buttonGroup.addButton(button => {
this.resetButton = button; // Сохраняем ссылку (хотя она пока не используется)
button
.setButtonText("Reset changes")
.setTooltip("Revert changes to the last applied settings")
.onClick(async () => {
// Просто вызываем переданный колбэк
await this.actions.onReset();
});
});
// Кнопка "Reset to defaults"
buttonGroup.addButton(button => {
this.resetDefaultsButton = button;
button
.setButtonText(this.defaultResetDefaultsText)
.setTooltip("Reset all settings to default values and apply immediately")
.onClick(async () => {
// Вызываем асинхронный колбэк (который внутри покажет ConfirmModal)
await this.actions.onResetDefaults();
});
});
// Кнопка "Apply Changes" (последняя для акцента)
buttonGroup.addButton(button => {
this.applyButton = button;
button
.setButtonText(this.defaultApplyText)
.setDisabled(true) // По умолчанию выключена
.onClick(async () => {
// Вызываем асинхронный колбэк
await this.actions.onApply();
});
});
}
/**
* Обновляет состояние кнопки "Apply Changes".
* @param options Параметры состояния.
*/
public setApplyState(options: ApplyButtonStateOptions): void {
if (!this.applyButton) return;
this.applyButton.setDisabled(options.disabled);
if (options.text !== undefined) {
this.applyButton.setButtonText(options.text);
} else {
// Возвращаем текст по умолчанию, если новый не задан
this.applyButton.setButtonText(this.defaultApplyText);
}
if (options.cta === true && !options.disabled) {
this.applyButton.setCta();
} else {
this.applyButton.removeCta();
}
}
/**
* Обновляет состояние кнопки "Reset to defaults".
* @param options Параметры состояния.
*/
public setResetDefaultsState(options: ResetDefaultsButtonStateOptions): void {
if (!this.resetDefaultsButton) return;
this.resetDefaultsButton.setDisabled(options.disabled);
if (options.text !== undefined) {
this.resetDefaultsButton.setButtonText(options.text);
} else {
// Возвращаем текст по умолчанию
this.resetDefaultsButton.setButtonText(this.defaultResetDefaultsText);
}
}
}