refactor: simplify modal handling and improve settings management

This commit is contained in:
2025-10-19 02:32:43 +03:00
parent 1deaab464b
commit 60e8f9a066
3 changed files with 97 additions and 250 deletions

View File

@@ -146,34 +146,22 @@ document.addEventListener('DOMContentLoaded', function () {
} }
if (helpLink) { if (helpLink) {
helpLink.addEventListener('click', function () { helpLink.addEventListener('click', () => ModalManager.open('help-modal'));
openHelpModal();
});
} }
const donateLink = document.getElementById('donate-link'); const donateLink = document.getElementById('donate-link');
if (donateLink) { if (donateLink) {
donateLink.addEventListener('click', function () { donateLink.addEventListener('click', () => ModalManager.open('donate-modal'));
openDonateModal();
});
} }
// Settings Modal // Settings Modal
const settingsLink = document.getElementById('settings-link'); const settingsLink = document.getElementById('settings-link');
const settingsModal = document.getElementById('settings-modal');
const settingsModalClose = document.getElementById('settings-modal-close');
const settingsApplyBtn = document.getElementById('settings-apply-btn'); const settingsApplyBtn = document.getElementById('settings-apply-btn');
const settingsResetBtn = document.getElementById('settings-reset-btn'); const settingsResetBtn = document.getElementById('settings-reset-btn');
const settingsCancelBtn = document.getElementById('settings-cancel-btn'); const settingsCancelBtn = document.getElementById('settings-cancel-btn');
if (settingsLink) { if (settingsLink) {
settingsLink.addEventListener('click', function () { settingsLink.addEventListener('click', openSettingsModal);
openSettingsModal();
});
}
if (settingsModalClose) {
settingsModalClose.addEventListener('click', closeSettingsModal);
} }
if (settingsCancelBtn) { if (settingsCancelBtn) {
@@ -194,14 +182,6 @@ document.addEventListener('DOMContentLoaded', function () {
}); });
} }
// Close on overlay click
if (settingsModal) {
settingsModal.addEventListener('click', function (e) {
if (e.target === settingsModal) {
closeSettingsModal();
}
});
}
// Settings UI interactions // Settings UI interactions
const fontSizeSlider = document.getElementById('setting-font-size'); const fontSizeSlider = document.getElementById('setting-font-size');
@@ -235,8 +215,6 @@ document.addEventListener('DOMContentLoaded', function () {
// Dataset Manager // Dataset Manager
const datasetsLink = document.getElementById('datasets-link'); const datasetsLink = document.getElementById('datasets-link');
const toggleDatasetsBtn = document.getElementById('toggle-datasets'); const toggleDatasetsBtn = document.getElementById('toggle-datasets');
const datasetModal = document.getElementById('dataset-modal');
const datasetModalClose = document.getElementById('dataset-modal-close');
const newDatasetBtn = document.getElementById('new-dataset-btn'); const newDatasetBtn = document.getElementById('new-dataset-btn');
const cancelDatasetBtn = document.getElementById('cancel-dataset-btn'); const cancelDatasetBtn = document.getElementById('cancel-dataset-btn');
const saveDatasetBtn = document.getElementById('save-dataset-btn'); const saveDatasetBtn = document.getElementById('save-dataset-btn');
@@ -251,20 +229,6 @@ document.addEventListener('DOMContentLoaded', function () {
toggleDatasetsBtn.addEventListener('click', openDatasetManager); toggleDatasetsBtn.addEventListener('click', openDatasetManager);
} }
// Close dataset manager
if (datasetModalClose) {
datasetModalClose.addEventListener('click', closeDatasetManager);
}
// Close on overlay click
if (datasetModal) {
datasetModal.addEventListener('click', function (e) {
if (e.target === datasetModal) {
closeDatasetManager();
}
});
}
// New dataset button // New dataset button
if (newDatasetBtn) { if (newDatasetBtn) {
newDatasetBtn.addEventListener('click', showNewDatasetForm); newDatasetBtn.addEventListener('click', showNewDatasetForm);
@@ -339,39 +303,21 @@ document.addEventListener('DOMContentLoaded', function () {
}); });
} }
// Help Modal // Global modal event delegation - handles close buttons and overlay clicks
const helpModal = document.getElementById('help-modal'); document.addEventListener('click', function(e) {
const helpModalClose = document.getElementById('help-modal-close'); // Handle modal close buttons (×)
if (e.target.id && e.target.id.endsWith('-modal-close')) {
const modalId = e.target.id.replace('-close', '');
ModalManager.close(modalId);
return;
}
if (helpModalClose) { // Handle overlay clicks (clicking outside modal content)
helpModalClose.addEventListener('click', closeHelpModal); if (e.target.classList.contains('modal')) {
} ModalManager.close(e.target.id);
return;
// Close on overlay click }
if (helpModal) { });
helpModal.addEventListener('click', function (e) {
if (e.target === helpModal) {
closeHelpModal();
}
});
}
// Donate Modal
const donateModal = document.getElementById('donate-modal');
const donateModalClose = document.getElementById('donate-modal-close');
if (donateModalClose) {
donateModalClose.addEventListener('click', closeDonateModal);
}
// Close on overlay click
if (donateModal) {
donateModal.addEventListener('click', function (e) {
if (e.target === donateModal) {
closeDonateModal();
}
});
}
// View mode toggle buttons // View mode toggle buttons
document.getElementById('view-draft').addEventListener('click', () => { document.getElementById('view-draft').addEventListener('click', () => {
@@ -406,14 +352,8 @@ document.addEventListener('DOMContentLoaded', function () {
} }
// Extract modal buttons // Extract modal buttons
const extractModalClose = document.getElementById('extract-modal-close');
const extractCancelBtn = document.getElementById('extract-cancel-btn'); const extractCancelBtn = document.getElementById('extract-cancel-btn');
const extractCreateBtn = document.getElementById('extract-create-btn'); const extractCreateBtn = document.getElementById('extract-create-btn');
const extractModal = document.getElementById('extract-modal');
if (extractModalClose) {
extractModalClose.addEventListener('click', hideExtractModal);
}
if (extractCancelBtn) { if (extractCancelBtn) {
extractCancelBtn.addEventListener('click', hideExtractModal); extractCancelBtn.addEventListener('click', hideExtractModal);
@@ -422,15 +362,6 @@ document.addEventListener('DOMContentLoaded', function () {
if (extractCreateBtn) { if (extractCreateBtn) {
extractCreateBtn.addEventListener('click', extractToDataset); extractCreateBtn.addEventListener('click', extractToDataset);
} }
// Close modal on overlay click
if (extractModal) {
extractModal.addEventListener('click', function (e) {
if (e.target === extractModal) {
hideExtractModal();
}
});
}
}); });
// Handle URL hash changes (browser back/forward) // Handle URL hash changes (browser back/forward)
@@ -495,8 +426,7 @@ const KeyboardActions = {
}, },
toggleSettings: function() { toggleSettings: function() {
const modal = document.getElementById('settings-modal'); if (ModalManager.isOpen('settings-modal')) {
if (modal && modal.style.display === 'flex') {
closeSettingsModal(); closeSettingsModal();
} else { } else {
openSettingsModal(); openSettingsModal();
@@ -504,30 +434,19 @@ const KeyboardActions = {
}, },
closeAnyModal: function() { closeAnyModal: function() {
const helpModal = document.getElementById('help-modal'); // Try ModalManager first for standard modals
const datasetModal = document.getElementById('dataset-modal'); if (ModalManager.closeAny()) {
const extractModal = document.getElementById('extract-modal');
const donateModal = document.getElementById('donate-modal');
const settingsModal = document.getElementById('settings-modal');
if (helpModal && helpModal.style.display === 'flex') {
closeHelpModal();
return true; return true;
} }
if (datasetModal && datasetModal.style.display === 'flex') { // Handle special modals with custom close logic
closeDatasetManager(); if (ModalManager.isOpen('extract-modal')) {
return true;
}
if (extractModal && extractModal.style.display === 'flex') {
hideExtractModal(); hideExtractModal();
return true; return true;
} }
if (donateModal && donateModal.style.display === 'flex') { // Dataset manager has special close logic (URL state)
closeDonateModal(); const datasetModal = document.getElementById('dataset-modal');
return true; if (datasetModal && datasetModal.style.display === 'flex') {
} closeDatasetManager();
if (settingsModal && settingsModal.style.display === 'flex') {
closeSettingsModal();
return true; return true;
} }
return false; return false;
@@ -594,56 +513,14 @@ function registerMonacoKeyboardShortcuts() {
KeyboardActions.publishDraft); KeyboardActions.publishDraft);
} }
// Help modal functions // Settings modal functions (special handling for loading settings into UI)
function openHelpModal() {
const modal = document.getElementById('help-modal');
if (modal) {
modal.style.display = 'flex';
// Track event
Analytics.track('modal-help', 'Open Help modal');
}
}
function closeHelpModal() {
const modal = document.getElementById('help-modal');
if (modal) {
modal.style.display = 'none';
}
}
// Donate modal functions
function openDonateModal() {
const modal = document.getElementById('donate-modal');
if (modal) {
modal.style.display = 'flex';
// Track event
Analytics.track('modal-donate', 'Open Donate modal');
}
}
function closeDonateModal() {
const modal = document.getElementById('donate-modal');
if (modal) {
modal.style.display = 'none';
}
}
// Settings modal functions
function openSettingsModal() { function openSettingsModal() {
loadSettingsIntoUI(); loadSettingsIntoUI();
const modal = document.getElementById('settings-modal'); ModalManager.open('settings-modal');
if (modal) {
modal.style.display = 'flex';
// Track event
Analytics.track('modal-settings', 'Open Settings modal');
}
} }
function closeSettingsModal() { function closeSettingsModal() {
const modal = document.getElementById('settings-modal'); ModalManager.close('settings-modal');
if (modal) {
modal.style.display = 'none';
}
} }
function loadSettingsIntoUI() { function loadSettingsIntoUI() {

View File

@@ -224,6 +224,52 @@ const Analytics = {
} }
}; };
// Modal Manager - Generic modal control utility
const ModalManager = {
// Track which events to send to analytics when opening modals
trackingMap: {
'help-modal': ['modal-help', 'Open Help modal'],
'donate-modal': ['modal-donate', 'Open Donate modal'],
'settings-modal': ['modal-settings', 'Open Settings modal'],
'dataset-modal': ['modal-dataset', 'Open Dataset Manager']
},
open(modalId, shouldTrack = true) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'flex';
if (shouldTrack && this.trackingMap[modalId]) {
const [event, title] = this.trackingMap[modalId];
Analytics.track(event, title);
}
}
},
close(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'none';
}
},
isOpen(modalId) {
const modal = document.getElementById(modalId);
return modal && modal.style.display === 'flex';
},
// Close any open modal (for ESC key handler)
closeAny() {
const modalIds = ['help-modal', 'donate-modal', 'settings-modal', 'dataset-modal', 'extract-modal'];
for (const modalId of modalIds) {
if (this.isOpen(modalId)) {
this.close(modalId);
return true;
}
}
return false;
}
};
// Shared utility: Format bytes for display // Shared utility: Format bytes for display
function formatBytes(bytes) { function formatBytes(bytes) {
if (bytes === null || bytes === undefined) return 'N/A'; if (bytes === null || bytes === undefined) return 'N/A';

View File

@@ -87,42 +87,24 @@ function getSettings() {
} }
// Get a specific setting by path (e.g., 'editor.fontSize') // Get a specific setting by path (e.g., 'editor.fontSize')
// Simplified: assumes 2-level structure (section.key)
function getSetting(path) { function getSetting(path) {
const settings = getSettings(); const settings = getSettings();
const parts = path.split('.'); const [section, key] = path.split('.');
let value = settings; return settings?.[section]?.[key];
for (const part of parts) {
if (value && typeof value === 'object' && part in value) {
value = value[part];
} else {
return undefined;
}
}
return value;
} }
// Update a specific setting by path // Update a specific setting by path
// Simplified: assumes 2-level structure (section.key)
function updateSetting(path, value) { function updateSetting(path, value) {
const settings = getSettings(); const settings = getSettings();
const parts = path.split('.'); const [section, key] = path.split('.');
let target = settings;
// Navigate to the parent object if (settings[section]) {
for (let i = 0; i < parts.length - 1; i++) { settings[section][key] = value;
const part = parts[i]; return saveSettings();
if (!target[part] || typeof target[part] !== 'object') {
target[part] = {};
}
target = target[part];
} }
return false;
// Set the value
const lastPart = parts[parts.length - 1];
target[lastPart] = value;
return saveSettings();
} }
// Update multiple settings at once // Update multiple settings at once
@@ -130,19 +112,10 @@ function updateSettings(updates) {
const settings = getSettings(); const settings = getSettings();
for (const path in updates) { for (const path in updates) {
const parts = path.split('.'); const [section, key] = path.split('.');
let target = settings; if (settings[section]) {
settings[section][key] = updates[path];
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
if (!target[part] || typeof target[part] !== 'object') {
target[part] = {};
}
target = target[part];
} }
const lastPart = parts[parts.length - 1];
target[lastPart] = updates[path];
} }
return saveSettings(); return saveSettings();
@@ -154,23 +127,6 @@ function resetSettings() {
return saveSettings(); return saveSettings();
} }
// Export settings as JSON
function exportSettings() {
return JSON.stringify(getSettings(), null, 2);
}
// Import settings from JSON string
function importSettings(jsonString) {
try {
const imported = JSON.parse(jsonString);
userSettings = mergeWithDefaults(imported);
return saveSettings();
} catch (error) {
console.error('Error importing settings:', error);
return false;
}
}
// Format date according to user settings // Format date according to user settings
function formatDate(isoString, useFullFormat = false) { function formatDate(isoString, useFullFormat = false) {
const date = new Date(isoString); const date = new Date(isoString);
@@ -253,48 +209,16 @@ function formatCustomDate(date, format) {
return result; return result;
} }
// Validate setting value // Validate setting value - simplified with inline validation rules
function validateSetting(path, value) { function validateSetting(path, value) {
const errors = []; const rules = {
'editor.fontSize': () => typeof value === 'number' && value >= 10 && value <= 18 ? [] : ['Font size must be between 10 and 18'],
'editor.theme': () => ['vs-light', 'vs-dark', 'hc-black'].includes(value) ? [] : ['Invalid editor theme'],
'editor.tabSize': () => typeof value === 'number' && value >= 2 && value <= 8 ? [] : ['Tab size must be between 2 and 8'],
'performance.renderDebounce': () => typeof value === 'number' && value >= 300 && value <= 3000 ? [] : ['Render debounce must be between 300-3000ms'],
'formatting.dateFormat': () => ['smart', 'locale', 'iso', 'custom'].includes(value) ? [] : ['Invalid date format'],
'ui.theme': () => ['light', 'experimental'].includes(value) ? [] : ['Invalid UI theme']
};
if (path === 'editor.fontSize') { return rules[path] ? rules[path]() : [];
if (typeof value !== 'number' || value < 10 || value > 18) {
errors.push('Font size must be between 10 and 18');
}
}
if (path === 'editor.theme') {
const validThemes = ['vs-light', 'vs-dark', 'hc-black'];
if (!validThemes.includes(value)) {
errors.push('Invalid theme value');
}
}
if (path === 'editor.tabSize') {
if (typeof value !== 'number' || value < 2 || value > 8) {
errors.push('Tab size must be between 2 and 8');
}
}
if (path === 'performance.renderDebounce') {
if (typeof value !== 'number' || value < 300 || value > 3000) {
errors.push('Render debounce must be between 300 and 3000ms');
}
}
if (path === 'formatting.dateFormat') {
const validFormats = ['smart', 'locale', 'iso', 'custom'];
if (!validFormats.includes(value)) {
errors.push('Invalid date format value');
}
}
if (path === 'ui.theme') {
const validThemes = ['light', 'experimental'];
if (!validThemes.includes(value)) {
errors.push('Invalid theme value');
}
}
return errors;
} }