diff --git a/src/ui/CheckboxSyncPluginSettingTab.ts b/src/ui/CheckboxSyncPluginSettingTab.ts index e19b2b1..5311d8b 100644 --- a/src/ui/CheckboxSyncPluginSettingTab.ts +++ b/src/ui/CheckboxSyncPluginSettingTab.ts @@ -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 }); } }); } diff --git a/src/ui/SettingsControls.ts b/src/ui/SettingsControls.ts new file mode 100644 index 0000000..43b2984 --- /dev/null +++ b/src/ui/SettingsControls.ts @@ -0,0 +1,122 @@ +import { ButtonComponent, Setting } from 'obsidian'; + +/** + * Интерфейс для действий, которые должны выполнять кнопки управления. + */ +export interface ISettingsControlActions { + onApply: () => Promise | void; + onReset: () => Promise | void; + onResetDefaults: () => Promise | 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); + } + } +} \ No newline at end of file