Add a square corners toggle for a border-radius-free interface
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import * as LucideIcons from 'lucide-react';
|
import * as LucideIcons from 'lucide-react';
|
||||||
const { Sun, Moon, User, LogOut, Check, Menu, Sparkles, Search } = LucideIcons;
|
const { Sun, Moon, User, LogOut, Check, Menu, Sparkles, Search, Radius } = LucideIcons;
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { CommandPalette } from '@/components/common/CommandPalette';
|
import { CommandPalette } from '@/components/common/CommandPalette';
|
||||||
import {
|
import {
|
||||||
@@ -43,6 +43,8 @@ export function TopBar() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const theme = useUIStore((s) => s.theme);
|
const theme = useUIStore((s) => s.theme);
|
||||||
const toggleTheme = useUIStore((s) => s.toggleTheme);
|
const toggleTheme = useUIStore((s) => s.toggleTheme);
|
||||||
|
const radius = useUIStore((s) => s.radius);
|
||||||
|
const toggleRadius = useUIStore((s) => s.toggleRadius);
|
||||||
const toggleSidebar = useUIStore((s) => s.toggleSidebar);
|
const toggleSidebar = useUIStore((s) => s.toggleSidebar);
|
||||||
const setActiveSection = useUIStore((s) => s.setActiveSection);
|
const setActiveSection = useUIStore((s) => s.setActiveSection);
|
||||||
const accounts = useAuthStore((s) => s.accounts);
|
const accounts = useAuthStore((s) => s.accounts);
|
||||||
@@ -189,6 +191,13 @@ export function TopBar() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<DropdownMenuItem onClick={toggleRadius}>
|
||||||
|
<Radius className="mr-2 h-4 w-4" />
|
||||||
|
{t('squareCorners', 'Square corners')}
|
||||||
|
{radius === 'square' && <Check className="ml-auto h-4 w-4" />}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const endSessionEndpoint = useAuthStore.getState().endSessionEndpoint;
|
const endSessionEndpoint = useAuthStore.getState().endSessionEndpoint;
|
||||||
|
|||||||
@@ -337,6 +337,7 @@
|
|||||||
"periodValue": "Period value"
|
"periodValue": "Period value"
|
||||||
},
|
},
|
||||||
"sections": "Sections",
|
"sections": "Sections",
|
||||||
|
"squareCorners": "Square corners",
|
||||||
"toggleTheme": "Toggle theme",
|
"toggleTheme": "Toggle theme",
|
||||||
"tracing": {
|
"tracing": {
|
||||||
"addFilter": "Add filter",
|
"addFilter": "Add filter",
|
||||||
|
|||||||
+9
-3
@@ -90,6 +90,12 @@
|
|||||||
--chart-5: 340 75% 60%;
|
--chart-5: 340 75% 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Square mode: every radius token derives multiplicatively from --radius,
|
||||||
|
so zeroing it here flattens all corners (rounded-full stays untouched). */
|
||||||
|
:root[data-radius='square'] {
|
||||||
|
--radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
@@ -127,10 +133,10 @@
|
|||||||
--color-chart-4: hsl(var(--chart-4));
|
--color-chart-4: hsl(var(--chart-4));
|
||||||
--color-chart-5: hsl(var(--chart-5));
|
--color-chart-5: hsl(var(--chart-5));
|
||||||
|
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
--radius-sm: calc(var(--radius) * 0.5);
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) * 0.75);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
--radius-xl: calc(var(--radius) * 1.5);
|
||||||
|
|
||||||
--animate-collapsible-down: collapsible-down 0.2s ease-out;
|
--animate-collapsible-down: collapsible-down 0.2s ease-out;
|
||||||
--animate-collapsible-up: collapsible-up 0.2s ease-out;
|
--animate-collapsible-up: collapsible-up 0.2s ease-out;
|
||||||
|
|||||||
@@ -8,14 +8,18 @@ import { create } from 'zustand';
|
|||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
type Theme = 'light' | 'dark';
|
type Theme = 'light' | 'dark';
|
||||||
|
type Radius = 'rounded' | 'square';
|
||||||
|
|
||||||
interface UIState {
|
interface UIState {
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
|
radius: Radius;
|
||||||
sidebarOpen: boolean;
|
sidebarOpen: boolean;
|
||||||
activeSection: string;
|
activeSection: string;
|
||||||
|
|
||||||
toggleTheme: () => void;
|
toggleTheme: () => void;
|
||||||
setTheme: (theme: Theme) => void;
|
setTheme: (theme: Theme) => void;
|
||||||
|
toggleRadius: () => void;
|
||||||
|
setRadius: (radius: Radius) => void;
|
||||||
toggleSidebar: () => void;
|
toggleSidebar: () => void;
|
||||||
setSidebarOpen: (open: boolean) => void;
|
setSidebarOpen: (open: boolean) => void;
|
||||||
setActiveSection: (section: string) => void;
|
setActiveSection: (section: string) => void;
|
||||||
@@ -29,11 +33,16 @@ function applyThemeClass(theme: Theme) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyRadiusAttribute(radius: Radius) {
|
||||||
|
document.documentElement.dataset.radius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
export const useUIStore = create<UIState>()(
|
export const useUIStore = create<UIState>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
theme:
|
theme:
|
||||||
typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',
|
typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',
|
||||||
|
radius: 'rounded',
|
||||||
sidebarOpen: typeof window !== 'undefined' ? (window.matchMedia?.('(min-width: 768px)').matches ?? true) : true,
|
sidebarOpen: typeof window !== 'undefined' ? (window.matchMedia?.('(min-width: 768px)').matches ?? true) : true,
|
||||||
activeSection: '',
|
activeSection: '',
|
||||||
|
|
||||||
@@ -48,6 +57,17 @@ export const useUIStore = create<UIState>()(
|
|||||||
set({ theme });
|
set({ theme });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggleRadius: () => {
|
||||||
|
const next = get().radius === 'rounded' ? 'square' : 'rounded';
|
||||||
|
applyRadiusAttribute(next);
|
||||||
|
set({ radius: next });
|
||||||
|
},
|
||||||
|
|
||||||
|
setRadius: (radius) => {
|
||||||
|
applyRadiusAttribute(radius);
|
||||||
|
set({ radius });
|
||||||
|
},
|
||||||
|
|
||||||
toggleSidebar: () => {
|
toggleSidebar: () => {
|
||||||
set({ sidebarOpen: !get().sidebarOpen });
|
set({ sidebarOpen: !get().sidebarOpen });
|
||||||
},
|
},
|
||||||
@@ -64,11 +84,13 @@ export const useUIStore = create<UIState>()(
|
|||||||
name: 'stalwart-ui',
|
name: 'stalwart-ui',
|
||||||
partialize: (state) => ({
|
partialize: (state) => ({
|
||||||
theme: state.theme,
|
theme: state.theme,
|
||||||
|
radius: state.radius,
|
||||||
}),
|
}),
|
||||||
onRehydrateStorage: () => {
|
onRehydrateStorage: () => {
|
||||||
return (state) => {
|
return (state) => {
|
||||||
if (state) {
|
if (state) {
|
||||||
applyThemeClass(state.theme);
|
applyThemeClass(state.theme);
|
||||||
|
applyRadiusAttribute(state.radius ?? 'rounded');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user