fixes
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState, useId } from 'react';
|
||||
import {
|
||||
ChevronIcon,
|
||||
TrashIcon,
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
PlusIcon,
|
||||
FolderPlusIcon,
|
||||
RestoreIcon,
|
||||
SunIcon,
|
||||
MoonIcon,
|
||||
DesktopIcon,
|
||||
} from '../ui/icons';
|
||||
import PanelHeader from '../ui/PanelHeader';
|
||||
|
||||
@@ -129,6 +132,13 @@ const FolderNode = ({
|
||||
);
|
||||
};
|
||||
|
||||
const THEME_MODES = ['system', 'light', 'dark'];
|
||||
const THEME_MODE_LABELS = {
|
||||
system: 'System',
|
||||
light: 'Light',
|
||||
dark: 'Dark',
|
||||
};
|
||||
|
||||
const Sidebar = ({
|
||||
folderNodes,
|
||||
onToggle,
|
||||
@@ -146,6 +156,8 @@ const Sidebar = ({
|
||||
creatingFolder = false,
|
||||
onNeutralHueChange,
|
||||
neutralHue,
|
||||
themeMode = 'system',
|
||||
onThemeModeChange,
|
||||
tags = [],
|
||||
activeTagIds = [],
|
||||
onToggleTagFilter,
|
||||
@@ -170,6 +182,7 @@ const Sidebar = ({
|
||||
onSelectTenant,
|
||||
onOpenSettings,
|
||||
}) => {
|
||||
const neutralHueInputId = useId();
|
||||
const sortedCorrespondents = useMemo(() => {
|
||||
if (!Array.isArray(correspondents)) {
|
||||
return [];
|
||||
@@ -230,6 +243,28 @@ const Sidebar = ({
|
||||
onNeutralHueChange?.('');
|
||||
}, [onNeutralHueChange]);
|
||||
|
||||
const themeModeIndex = THEME_MODES.indexOf(themeMode);
|
||||
const safeThemeModeIndex = themeModeIndex === -1 ? 0 : themeModeIndex;
|
||||
const resolvedThemeMode = THEME_MODES[safeThemeModeIndex];
|
||||
const nextThemeMode = THEME_MODES[(safeThemeModeIndex + 1) % THEME_MODES.length];
|
||||
const themeModeLabel = THEME_MODE_LABELS[resolvedThemeMode];
|
||||
const nextThemeLabel = THEME_MODE_LABELS[nextThemeMode];
|
||||
const themeModeIcon = resolvedThemeMode === 'dark'
|
||||
? <MoonIcon size={16} />
|
||||
: resolvedThemeMode === 'light'
|
||||
? <SunIcon size={16} />
|
||||
: <DesktopIcon size={16} />;
|
||||
|
||||
const handleThemeModeToggle = useCallback(() => {
|
||||
if (!onThemeModeChange) {
|
||||
return;
|
||||
}
|
||||
const currentIndex = THEME_MODES.indexOf(themeMode);
|
||||
const safeIndex = currentIndex === -1 ? 0 : currentIndex;
|
||||
const nextMode = THEME_MODES[(safeIndex + 1) % THEME_MODES.length];
|
||||
onThemeModeChange(nextMode);
|
||||
}, [onThemeModeChange, themeMode]);
|
||||
|
||||
const handleSearchInputChange = useCallback(
|
||||
(event) => {
|
||||
onSearchChange?.(event.target.value);
|
||||
@@ -350,6 +385,50 @@ const Sidebar = ({
|
||||
);
|
||||
|
||||
const rootNode = folderNodes.get('root');
|
||||
const themeControls =
|
||||
typeof neutralHue === 'number' || typeof neutralHue === 'string'
|
||||
? (
|
||||
<div className="sidebar-section sidebar-theme">
|
||||
<div className="sidebar-section__header">
|
||||
<h3>Theme</h3>
|
||||
<div className="sidebar-section__actions">
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={handleThemeModeToggle}
|
||||
aria-label={`Switch theme (next: ${nextThemeLabel})`}
|
||||
title={`Theme: ${themeModeLabel} (next: ${nextThemeLabel})`}
|
||||
>
|
||||
{themeModeIcon}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={handleNeutralHueReset}
|
||||
aria-label="Reset neutral hue"
|
||||
title="Reset neutral hue"
|
||||
>
|
||||
<RestoreIcon size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<label className="sidebar-slider" htmlFor={neutralHueInputId}>
|
||||
<span className="sidebar-slider__label">Hue</span>
|
||||
<input
|
||||
id={neutralHueInputId}
|
||||
type="range"
|
||||
min="0"
|
||||
max="360"
|
||||
step="1"
|
||||
value={neutralHue}
|
||||
onChange={(event) => onNeutralHueChange?.(event.target.value)}
|
||||
/>
|
||||
<span className="sidebar-slider__value">{neutralHue}°</span>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<aside className="sidebar">
|
||||
<PanelHeader
|
||||
@@ -606,36 +685,8 @@ const Sidebar = ({
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
{typeof neutralHue === 'number' || typeof neutralHue === 'string' ? (
|
||||
<div className="sidebar-section">
|
||||
<div className="sidebar-section__header">
|
||||
<h3>Theme</h3>
|
||||
<div className="sidebar-section__actions">
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={handleNeutralHueReset}
|
||||
aria-label="Reset neutral hue"
|
||||
>
|
||||
<RestoreIcon size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<label className="sidebar-slider">
|
||||
<span className="sidebar-slider__label">Neutral hue</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="360"
|
||||
step="1"
|
||||
value={neutralHue}
|
||||
onChange={(event) => onNeutralHueChange?.(event.target.value)}
|
||||
/>
|
||||
<span className="sidebar-slider__value">{neutralHue}°</span>
|
||||
</label>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{themeControls ? <div className="sidebar__footer">{themeControls}</div> : null}
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user