fix: settings navigation
This commit is contained in:
@@ -79,69 +79,43 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Body layout */
|
/* Body layout - simplified, no internal sidebar */
|
||||||
.settings-body {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Category navigation */
|
|
||||||
.settings-nav {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 180px;
|
|
||||||
min-width: 180px;
|
|
||||||
padding: 12px 0;
|
|
||||||
border-right: 1px solid var(--vscode-panel-border);
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
border-left: 2px solid transparent;
|
|
||||||
color: var(--vscode-foreground);
|
|
||||||
font-size: 13px;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
transition: background-color 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-item:hover {
|
|
||||||
background-color: var(--vscode-list-hoverBackground);
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-item.active {
|
|
||||||
background-color: var(--vscode-list-activeSelectionBackground);
|
|
||||||
border-left-color: var(--vscode-focusBorder);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-icon {
|
|
||||||
font-size: 16px;
|
|
||||||
width: 20px;
|
|
||||||
text-align: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-label {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Settings content area */
|
|
||||||
.settings-content {
|
.settings-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 16px 24px 40px;
|
padding: 16px 24px 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* No results message */
|
||||||
|
.settings-no-results {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 48px 24px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-no-results p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-no-results button {
|
||||||
|
padding: 6px 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--vscode-button-secondaryBackground);
|
||||||
|
color: var(--vscode-button-secondaryForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-no-results button:hover {
|
||||||
|
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
/* Setting section */
|
/* Setting section */
|
||||||
.setting-section {
|
.setting-section {
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
@@ -431,23 +405,4 @@
|
|||||||
color: var(--vscode-input-placeholderForeground);
|
color: var(--vscode-input-placeholderForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive - narrow sidebar */
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.settings-nav {
|
|
||||||
width: 48px;
|
|
||||||
min-width: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-label {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-item {
|
|
||||||
justify-content: center;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-icon {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { useAppStore } from '../../store';
|
import { useAppStore } from '../../store';
|
||||||
import { showToast } from '../Toast';
|
import { showToast } from '../Toast';
|
||||||
import './SettingsView.css';
|
import './SettingsView.css';
|
||||||
|
|
||||||
// Settings categories matching VS Code style
|
// Export category IDs for sidebar navigation
|
||||||
type SettingsCategory = 'editor' | 'content' | 'sync' | 'publishing' | 'data';
|
export type SettingsCategory = 'editor' | 'content' | 'sync' | 'publishing' | 'data';
|
||||||
|
|
||||||
|
// Scroll to a settings section by category ID
|
||||||
|
export const scrollToSettingsSection = (category: SettingsCategory) => {
|
||||||
|
const element = document.getElementById(`settings-section-${category}`);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Settings categories
|
||||||
|
|
||||||
interface Credentials {
|
interface Credentials {
|
||||||
// Turso Cloud Sync
|
// Turso Cloud Sync
|
||||||
@@ -48,15 +58,6 @@ const SearchIcon = () => (
|
|||||||
// Default post categories based on VISION.md
|
// Default post categories based on VISION.md
|
||||||
const DEFAULT_POST_CATEGORIES = ['article', 'picture', 'aside', 'page'];
|
const DEFAULT_POST_CATEGORIES = ['article', 'picture', 'aside', 'page'];
|
||||||
|
|
||||||
// Category definitions
|
|
||||||
const categories: { id: SettingsCategory; label: string; icon: string }[] = [
|
|
||||||
{ id: 'editor', label: 'Editor', icon: '📝' },
|
|
||||||
{ id: 'content', label: 'Content', icon: '📋' },
|
|
||||||
{ id: 'sync', label: 'Sync', icon: '🔄' },
|
|
||||||
{ id: 'publishing', label: 'Publishing', icon: '🚀' },
|
|
||||||
{ id: 'data', label: 'Data Management', icon: '🗄️' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Individual setting row component (VS Code style)
|
// Individual setting row component (VS Code style)
|
||||||
const SettingRow: React.FC<{
|
const SettingRow: React.FC<{
|
||||||
id: string;
|
id: string;
|
||||||
@@ -75,13 +76,17 @@ const SettingRow: React.FC<{
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Section header component
|
// Section header component with optional ID for scrolling
|
||||||
const SettingSection: React.FC<{
|
const SettingSection: React.FC<{
|
||||||
|
id?: string;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}> = ({ title, description, children }) => (
|
hidden?: boolean;
|
||||||
<div className="setting-section">
|
}> = ({ id, title, description, children, hidden }) => {
|
||||||
|
if (hidden) return null;
|
||||||
|
return (
|
||||||
|
<div className="setting-section" id={id}>
|
||||||
<div className="setting-section-header">
|
<div className="setting-section-header">
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
{description && <p className="setting-section-description">{description}</p>}
|
{description && <p className="setting-section-description">{description}</p>}
|
||||||
@@ -90,21 +95,36 @@ const SettingSection: React.FC<{
|
|||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const SettingsView: React.FC = () => {
|
export const SettingsView: React.FC = () => {
|
||||||
const { preferredEditorMode, setPreferredEditorMode, syncConfigured } = useAppStore();
|
const { preferredEditorMode, setPreferredEditorMode, syncConfigured } = useAppStore();
|
||||||
const [activeCategory, setActiveCategory] = useState<SettingsCategory>('editor');
|
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [credentials, setCredentials] = useState<Credentials>(defaultCredentials);
|
const [credentials, setCredentials] = useState<Credentials>(defaultCredentials);
|
||||||
const [showSecrets, setShowSecrets] = useState(false);
|
const [showSecrets, setShowSecrets] = useState(false);
|
||||||
const [dropboxConfigured, setDropboxConfigured] = useState(false);
|
const [dropboxConfigured, setDropboxConfigured] = useState(false);
|
||||||
const [dropboxLastSync, setDropboxLastSync] = useState<string | null>(null);
|
const [dropboxLastSync, setDropboxLastSync] = useState<string | null>(null);
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Post categories management
|
// Post categories management
|
||||||
const [postCategories, setPostCategories] = useState<string[]>(DEFAULT_POST_CATEGORIES);
|
const [postCategories, setPostCategories] = useState<string[]>(DEFAULT_POST_CATEGORIES);
|
||||||
const [newCategoryInput, setNewCategoryInput] = useState('');
|
const [newCategoryInput, setNewCategoryInput] = useState('');
|
||||||
|
|
||||||
|
// Check if a setting matches the search query
|
||||||
|
const matchesSearch = useCallback((text: string) => {
|
||||||
|
if (!searchQuery) return true;
|
||||||
|
return text.toLowerCase().includes(searchQuery.toLowerCase());
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
|
// Check if a section has any matching settings
|
||||||
|
const sectionHasMatches = useCallback((sectionKeywords: string[]) => {
|
||||||
|
if (!searchQuery) return true;
|
||||||
|
return sectionKeywords.some(keyword =>
|
||||||
|
keyword.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
}, [searchQuery]);
|
||||||
|
|
||||||
// Load saved credentials and categories
|
// Load saved credentials and categories
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadSettings = async () => {
|
const loadSettings = async () => {
|
||||||
@@ -261,16 +281,19 @@ export const SettingsView: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter categories if searching
|
// Keywords for each section for search filtering
|
||||||
const filteredCategories = searchQuery
|
const editorKeywords = ['editor', 'mode', 'wysiwyg', 'markdown', 'preview', 'visual'];
|
||||||
? categories.filter(c => c.label.toLowerCase().includes(searchQuery.toLowerCase()))
|
const contentKeywords = ['content', 'categories', 'post', 'article', 'picture', 'aside', 'page'];
|
||||||
: categories;
|
const syncKeywords = ['sync', 'turso', 'libsql', 'cloud', 'database', 'dropbox', 'file', 'backup', 'token', 'remote'];
|
||||||
|
const publishingKeywords = ['publishing', 'ftp', 'ssh', 'deploy', 'server', 'host', 'upload'];
|
||||||
|
const dataKeywords = ['data', 'database', 'rebuild', 'maintenance', 'posts', 'media', 'links', 'folder', 'filesystem'];
|
||||||
|
|
||||||
const renderEditorSettings = () => (
|
const renderEditorSettings = () => (
|
||||||
<>
|
|
||||||
<SettingSection
|
<SettingSection
|
||||||
|
id="settings-section-editor"
|
||||||
title="Editor"
|
title="Editor"
|
||||||
description="Configure the blog post editor behavior and appearance."
|
description="Configure the blog post editor behavior and appearance."
|
||||||
|
hidden={!sectionHasMatches(editorKeywords)}
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
id="editor-mode"
|
id="editor-mode"
|
||||||
@@ -288,7 +311,6 @@ export const SettingsView: React.FC = () => {
|
|||||||
</select>
|
</select>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
</SettingSection>
|
</SettingSection>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handlers for post categories management
|
// Handlers for post categories management
|
||||||
@@ -323,10 +345,11 @@ export const SettingsView: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderContentSettings = () => (
|
const renderContentSettings = () => (
|
||||||
<>
|
|
||||||
<SettingSection
|
<SettingSection
|
||||||
|
id="settings-section-content"
|
||||||
title="Post Categories"
|
title="Post Categories"
|
||||||
description="Manage the available categories for blog posts. Each post can have one category that determines its display template."
|
description="Manage the available categories for blog posts. Each post can have one category that determines its display template."
|
||||||
|
hidden={!sectionHasMatches(contentKeywords)}
|
||||||
>
|
>
|
||||||
<div className="categories-list">
|
<div className="categories-list">
|
||||||
{postCategories.map((cat) => (
|
{postCategories.map((cat) => (
|
||||||
@@ -367,14 +390,15 @@ export const SettingsView: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</SettingSection>
|
</SettingSection>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderSyncSettings = () => (
|
const renderSyncSettings = () => (
|
||||||
<>
|
<>
|
||||||
<SettingSection
|
<SettingSection
|
||||||
|
id="settings-section-sync"
|
||||||
title="Cloud Sync — Turso/LibSQL"
|
title="Cloud Sync — Turso/LibSQL"
|
||||||
description="Sync post and media metadata to a Turso cloud database for backup and multi-device access."
|
description="Sync post and media metadata to a Turso cloud database for backup and multi-device access."
|
||||||
|
hidden={!sectionHasMatches(syncKeywords)}
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
id="turso-url"
|
id="turso-url"
|
||||||
@@ -436,6 +460,7 @@ export const SettingsView: React.FC = () => {
|
|||||||
<SettingSection
|
<SettingSection
|
||||||
title="File Sync — Dropbox"
|
title="File Sync — Dropbox"
|
||||||
description="Synchronize your blog files (posts and media) to Dropbox for backup and cross-device access."
|
description="Synchronize your blog files (posts and media) to Dropbox for backup and cross-device access."
|
||||||
|
hidden={!sectionHasMatches(syncKeywords)}
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
id="dropbox-token"
|
id="dropbox-token"
|
||||||
@@ -523,8 +548,10 @@ export const SettingsView: React.FC = () => {
|
|||||||
const renderPublishingSettings = () => (
|
const renderPublishingSettings = () => (
|
||||||
<>
|
<>
|
||||||
<SettingSection
|
<SettingSection
|
||||||
|
id="settings-section-publishing"
|
||||||
title="FTP Publishing"
|
title="FTP Publishing"
|
||||||
description="Configure FTP credentials for publishing your blog to a web server."
|
description="Configure FTP credentials for publishing your blog to a web server."
|
||||||
|
hidden={!sectionHasMatches(publishingKeywords)}
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
id="ftp-host"
|
id="ftp-host"
|
||||||
@@ -577,6 +604,7 @@ export const SettingsView: React.FC = () => {
|
|||||||
<SettingSection
|
<SettingSection
|
||||||
title="SSH Publishing"
|
title="SSH Publishing"
|
||||||
description="Configure SSH credentials for secure deployment to your server."
|
description="Configure SSH credentials for secure deployment to your server."
|
||||||
|
hidden={!sectionHasMatches(publishingKeywords)}
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
id="ssh-host"
|
id="ssh-host"
|
||||||
@@ -631,8 +659,10 @@ export const SettingsView: React.FC = () => {
|
|||||||
const renderDataSettings = () => (
|
const renderDataSettings = () => (
|
||||||
<>
|
<>
|
||||||
<SettingSection
|
<SettingSection
|
||||||
|
id="settings-section-data"
|
||||||
title="Database Maintenance"
|
title="Database Maintenance"
|
||||||
description="Rebuild the local database index from source files. Useful if post or media files were edited externally."
|
description="Rebuild the local database index from source files. Useful if post or media files were edited externally."
|
||||||
|
hidden={!sectionHasMatches(dataKeywords)}
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
id="rebuild-posts"
|
id="rebuild-posts"
|
||||||
@@ -715,6 +745,7 @@ export const SettingsView: React.FC = () => {
|
|||||||
<SettingSection
|
<SettingSection
|
||||||
title="File System"
|
title="File System"
|
||||||
description="Access project data files and directories."
|
description="Access project data files and directories."
|
||||||
|
hidden={!sectionHasMatches(dataKeywords)}
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
id="open-data"
|
id="open-data"
|
||||||
@@ -737,35 +768,13 @@ export const SettingsView: React.FC = () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderContent = () => {
|
// Check if any results match the search
|
||||||
if (searchQuery) {
|
const hasAnyMatches = !searchQuery ||
|
||||||
// Show all matching settings when searching
|
sectionHasMatches(editorKeywords) ||
|
||||||
return (
|
sectionHasMatches(contentKeywords) ||
|
||||||
<>
|
sectionHasMatches(syncKeywords) ||
|
||||||
{renderEditorSettings()}
|
sectionHasMatches(publishingKeywords) ||
|
||||||
{renderContentSettings()}
|
sectionHasMatches(dataKeywords);
|
||||||
{renderSyncSettings()}
|
|
||||||
{renderPublishingSettings()}
|
|
||||||
{renderDataSettings()}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (activeCategory) {
|
|
||||||
case 'editor':
|
|
||||||
return renderEditorSettings();
|
|
||||||
case 'content':
|
|
||||||
return renderContentSettings();
|
|
||||||
case 'sync':
|
|
||||||
return renderSyncSettings();
|
|
||||||
case 'publishing':
|
|
||||||
return renderPublishingSettings();
|
|
||||||
case 'data':
|
|
||||||
return renderDataSettings();
|
|
||||||
default:
|
|
||||||
return renderEditorSettings();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-view">
|
<div className="settings-view">
|
||||||
@@ -791,28 +800,22 @@ export const SettingsView: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-body">
|
{/* Settings content - all sections in scrollable list */}
|
||||||
{/* Category navigation sidebar */}
|
<div className="settings-content" ref={contentRef}>
|
||||||
<nav className="settings-nav">
|
{hasAnyMatches ? (
|
||||||
{filteredCategories.map((cat) => (
|
<>
|
||||||
<button
|
{renderEditorSettings()}
|
||||||
key={cat.id}
|
{renderContentSettings()}
|
||||||
className={`settings-nav-item ${activeCategory === cat.id && !searchQuery ? 'active' : ''}`}
|
{renderSyncSettings()}
|
||||||
onClick={() => {
|
{renderPublishingSettings()}
|
||||||
setActiveCategory(cat.id);
|
{renderDataSettings()}
|
||||||
setSearchQuery('');
|
</>
|
||||||
}}
|
) : (
|
||||||
>
|
<div className="settings-no-results">
|
||||||
<span className="settings-nav-icon">{cat.icon}</span>
|
<p>No settings found matching "{searchQuery}"</p>
|
||||||
<span className="settings-nav-label">{cat.label}</span>
|
<button onClick={() => setSearchQuery('')}>Clear search</button>
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{/* Settings content */}
|
|
||||||
<div className="settings-content">
|
|
||||||
{renderContent()}
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
export { SettingsView } from './SettingsView';
|
export { SettingsView, scrollToSettingsSection } from './SettingsView';
|
||||||
|
export type { SettingsCategory } from './SettingsView';
|
||||||
|
|||||||
@@ -216,6 +216,21 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--vscode-sideBar-foreground);
|
color: var(--vscode-sideBar-foreground);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
transition: background-color 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-nav-entry:hover {
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-nav-entry.active {
|
||||||
|
background-color: var(--vscode-list-activeSelectionBackground);
|
||||||
|
color: var(--vscode-list-activeSelectionForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-nav-entry-icon {
|
.settings-nav-entry-icon {
|
||||||
|
|||||||
@@ -578,8 +578,16 @@ const MediaList: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import { scrollToSettingsSection, SettingsCategory } from '../SettingsView/SettingsView';
|
||||||
|
|
||||||
const SettingsNav: React.FC = () => {
|
const SettingsNav: React.FC = () => {
|
||||||
const { syncConfigured } = useAppStore();
|
const { syncConfigured } = useAppStore();
|
||||||
|
const [activeSection, setActiveSection] = useState<SettingsCategory | null>(null);
|
||||||
|
|
||||||
|
const handleNavClick = (category: SettingsCategory) => {
|
||||||
|
setActiveSection(category);
|
||||||
|
scrollToSettingsSection(category);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sidebar-content settings-panel">
|
<div className="sidebar-content settings-panel">
|
||||||
@@ -590,27 +598,44 @@ const SettingsNav: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-nav-list">
|
<div className="settings-nav-list">
|
||||||
<div className="settings-nav-entry">
|
<button
|
||||||
|
className={`settings-nav-entry ${activeSection === 'editor' ? 'active' : ''}`}
|
||||||
|
onClick={() => handleNavClick('editor')}
|
||||||
|
>
|
||||||
<span className="settings-nav-entry-icon">📝</span>
|
<span className="settings-nav-entry-icon">📝</span>
|
||||||
<span>Editor</span>
|
<span>Editor</span>
|
||||||
</div>
|
</button>
|
||||||
<div className="settings-nav-entry">
|
<button
|
||||||
|
className={`settings-nav-entry ${activeSection === 'content' ? 'active' : ''}`}
|
||||||
|
onClick={() => handleNavClick('content')}
|
||||||
|
>
|
||||||
|
<span className="settings-nav-entry-icon">📋</span>
|
||||||
|
<span>Content</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`settings-nav-entry ${activeSection === 'sync' ? 'active' : ''}`}
|
||||||
|
onClick={() => handleNavClick('sync')}
|
||||||
|
>
|
||||||
<span className="settings-nav-entry-icon">🔄</span>
|
<span className="settings-nav-entry-icon">🔄</span>
|
||||||
<span>Sync</span>
|
<span>Sync</span>
|
||||||
{syncConfigured && <span className="settings-nav-badge">✓</span>}
|
{syncConfigured && <span className="settings-nav-badge">✓</span>}
|
||||||
</div>
|
</button>
|
||||||
<div className="settings-nav-entry">
|
<button
|
||||||
|
className={`settings-nav-entry ${activeSection === 'publishing' ? 'active' : ''}`}
|
||||||
|
onClick={() => handleNavClick('publishing')}
|
||||||
|
>
|
||||||
<span className="settings-nav-entry-icon">🚀</span>
|
<span className="settings-nav-entry-icon">🚀</span>
|
||||||
<span>Publishing</span>
|
<span>Publishing</span>
|
||||||
</div>
|
</button>
|
||||||
<div className="settings-nav-entry">
|
<button
|
||||||
|
className={`settings-nav-entry ${activeSection === 'data' ? 'active' : ''}`}
|
||||||
|
onClick={() => handleNavClick('data')}
|
||||||
|
>
|
||||||
<span className="settings-nav-entry-icon">🗄️</span>
|
<span className="settings-nav-entry-icon">🗄️</span>
|
||||||
<span>Data</span>
|
<span>Data</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="settings-hint">Configure settings in the main editor area.</p>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user