This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { type ThemePref, readStoredTheme, setThemePref } from '../../lib/theme';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { type ThemePref, isThemePref, readStoredTheme, setThemePref, THEME_CHANGE_EVENT } from '../../lib/theme';
|
||||
|
||||
const ICON_PROPS = {
|
||||
width: 14,
|
||||
@@ -56,6 +56,17 @@ const OPTIONS: Array<{ value: ThemePref; label: string; icon: JSX.Element }> = [
|
||||
export function ThemeToggle() {
|
||||
const [pref, setPref] = useState<ThemePref>(() => readStoredTheme());
|
||||
|
||||
useEffect(() => {
|
||||
const sync = (e: Event) => {
|
||||
// Prefer the pref carried in the event (works even if localStorage write
|
||||
// failed); fall back to stored value for plain Event dispatches.
|
||||
const detail = (e as CustomEvent<unknown>).detail;
|
||||
setPref(isThemePref(detail) ? detail : readStoredTheme());
|
||||
};
|
||||
window.addEventListener(THEME_CHANGE_EVENT, sync);
|
||||
return () => window.removeEventListener(THEME_CHANGE_EVENT, sync);
|
||||
}, []);
|
||||
|
||||
const choose = (value: ThemePref) => {
|
||||
setPref(value);
|
||||
setThemePref(value);
|
||||
|
||||
@@ -14,6 +14,7 @@ interface TopBarProps {
|
||||
onOpenDrawer: () => void;
|
||||
hamburgerButtonRef?: React.RefObject<HTMLButtonElement>;
|
||||
navDrawerOpen?: boolean;
|
||||
onOpenCommandK?: () => void;
|
||||
}
|
||||
|
||||
export const NAV_ITEMS: Array<{ id: PageId; label: string; adminOnly: boolean; requiresAuth: boolean }> = [
|
||||
@@ -69,6 +70,7 @@ export function TopBar({
|
||||
onOpenDrawer,
|
||||
hamburgerButtonRef,
|
||||
navDrawerOpen = false,
|
||||
onOpenCommandK,
|
||||
}: TopBarProps) {
|
||||
const visibleNav = visibleNavItemsFor(isAdmin, authEnabled);
|
||||
const compactMode = useViewportNarrow(estimateCollapseThreshold(visibleNav.length));
|
||||
@@ -138,6 +140,17 @@ export function TopBar({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{onOpenCommandK && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenCommandK}
|
||||
aria-label="コマンドパレットを開く"
|
||||
title="コマンドパレット (⌘K)"
|
||||
className="hidden sm:inline-flex items-center gap-1 px-2 h-7 rounded-md border border-hairline text-2xs text-slate-500 hover:text-slate-800 hover:bg-surface transition-colors"
|
||||
>
|
||||
<span aria-hidden>⌘K</span>
|
||||
</button>
|
||||
)}
|
||||
<ThemeToggle />
|
||||
{user && (
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
Reference in New Issue
Block a user