mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-05 23:44:22 +00:00
Refactor theme styles for hover, accordion, and workspace components
This commit is contained in:
@@ -18,6 +18,7 @@ import { useAuth } from '../../contexts/AuthContext';
|
|||||||
import AccountSettings from '../settings/account/AccountSettings';
|
import AccountSettings from '../settings/account/AccountSettings';
|
||||||
import AdminDashboard from '../settings/admin/AdminDashboard';
|
import AdminDashboard from '../settings/admin/AdminDashboard';
|
||||||
import { UserRole } from '@/types/models';
|
import { UserRole } from '@/types/models';
|
||||||
|
import { getHoverStyle } from '@/utils/themeStyles';
|
||||||
|
|
||||||
const UserMenu: React.FC = () => {
|
const UserMenu: React.FC = () => {
|
||||||
const [accountSettingsOpened, setAccountSettingsOpened] =
|
const [accountSettingsOpened, setAccountSettingsOpened] =
|
||||||
@@ -75,15 +76,7 @@ const UserMenu: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
px="sm"
|
px="sm"
|
||||||
py="xs"
|
py="xs"
|
||||||
style={(theme: any) => ({
|
style={(theme) => getHoverStyle(theme)}
|
||||||
borderRadius: theme.radius.sm,
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor:
|
|
||||||
theme.colorScheme === 'dark'
|
|
||||||
? theme.colors.dark[5]
|
|
||||||
: theme.colors.gray[0],
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<Group>
|
<Group>
|
||||||
<IconSettings size={16} />
|
<IconSettings size={16} />
|
||||||
@@ -99,15 +92,7 @@ const UserMenu: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
px="sm"
|
px="sm"
|
||||||
py="xs"
|
py="xs"
|
||||||
style={(theme: any) => ({
|
style={(theme) => getHoverStyle(theme)}
|
||||||
borderRadius: theme.radius.sm,
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor:
|
|
||||||
theme.colorScheme === 'dark'
|
|
||||||
? theme.colors.dark[5]
|
|
||||||
: theme.colors.gray[0],
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<Group>
|
<Group>
|
||||||
<IconUsers size={16} />
|
<IconUsers size={16} />
|
||||||
@@ -121,15 +106,7 @@ const UserMenu: React.FC = () => {
|
|||||||
px="sm"
|
px="sm"
|
||||||
py="xs"
|
py="xs"
|
||||||
color="red"
|
color="red"
|
||||||
style={(theme: any) => ({
|
style={(theme) => getHoverStyle(theme)}
|
||||||
borderRadius: theme.radius.sm,
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor:
|
|
||||||
theme.colorScheme === 'dark'
|
|
||||||
? theme.colors.dark[5]
|
|
||||||
: theme.colors.gray[0],
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<Group>
|
<Group>
|
||||||
<IconLogout size={16} color="red" />
|
<IconLogout size={16} color="red" />
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ import { useModalContext } from '../../contexts/ModalContext';
|
|||||||
import { listWorkspaces } from '../../api/workspace';
|
import { listWorkspaces } from '../../api/workspace';
|
||||||
import CreateWorkspaceModal from '../modals/workspace/CreateWorkspaceModal';
|
import CreateWorkspaceModal from '../modals/workspace/CreateWorkspaceModal';
|
||||||
import type { Workspace } from '@/types/models';
|
import type { Workspace } from '@/types/models';
|
||||||
|
import {
|
||||||
|
getConditionalColor,
|
||||||
|
getWorkspacePaperStyle,
|
||||||
|
} from '@/utils/themeStyles';
|
||||||
|
|
||||||
const WorkspaceSwitcher: React.FC = () => {
|
const WorkspaceSwitcher: React.FC = () => {
|
||||||
const { currentWorkspace, switchWorkspace } = useWorkspace();
|
const { currentWorkspace, switchWorkspace } = useWorkspace();
|
||||||
@@ -111,18 +115,9 @@ const WorkspaceSwitcher: React.FC = () => {
|
|||||||
key={workspace.name}
|
key={workspace.name}
|
||||||
p="xs"
|
p="xs"
|
||||||
withBorder
|
withBorder
|
||||||
style={(theme: any) => ({
|
style={(theme) =>
|
||||||
backgroundColor: isSelected
|
getWorkspacePaperStyle(theme, isSelected)
|
||||||
? theme.colors.blue[
|
}
|
||||||
theme.colorScheme === 'dark' ? 8 : 1
|
|
||||||
]
|
|
||||||
: undefined,
|
|
||||||
borderColor: isSelected
|
|
||||||
? theme.colors.blue[
|
|
||||||
theme.colorScheme === 'dark' ? 7 : 5
|
|
||||||
]
|
|
||||||
: undefined,
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<Group justify="space-between" wrap="nowrap">
|
<Group justify="space-between" wrap="nowrap">
|
||||||
<UnstyledButton
|
<UnstyledButton
|
||||||
@@ -137,27 +132,13 @@ const WorkspaceSwitcher: React.FC = () => {
|
|||||||
size="sm"
|
size="sm"
|
||||||
fw={500}
|
fw={500}
|
||||||
truncate
|
truncate
|
||||||
c={
|
c={isSelected ? 'blue' : 'inherit'}
|
||||||
isSelected
|
|
||||||
? (theme as any).colors.blue[
|
|
||||||
(theme as any).colorScheme === 'dark'
|
|
||||||
? 0
|
|
||||||
: 9
|
|
||||||
]
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{workspace.name}
|
{workspace.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
size="xs"
|
size="xs"
|
||||||
c={
|
c={getConditionalColor(theme, isSelected)}
|
||||||
isSelected
|
|
||||||
? (theme as any).colorScheme === 'dark'
|
|
||||||
? theme.colors.blue[2]
|
|
||||||
: theme.colors.blue[7]
|
|
||||||
: 'dimmed'
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{new Date(
|
{new Date(
|
||||||
workspace.createdAt
|
workspace.createdAt
|
||||||
@@ -170,11 +151,7 @@ const WorkspaceSwitcher: React.FC = () => {
|
|||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
size="lg"
|
size="lg"
|
||||||
color={
|
color={getConditionalColor(theme, true)}
|
||||||
(theme as any).colorScheme === 'dark'
|
|
||||||
? 'blue.2'
|
|
||||||
: 'blue.7'
|
|
||||||
}
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSettingsModalVisible(true);
|
setSettingsModalVisible(true);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
type SettingsAction,
|
type SettingsAction,
|
||||||
SettingsActionType,
|
SettingsActionType,
|
||||||
} from '@/types/models';
|
} from '@/types/models';
|
||||||
|
import { getAccordionStyles } from '@/utils/themeStyles';
|
||||||
|
|
||||||
interface AccountSettingsProps {
|
interface AccountSettingsProps {
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
@@ -202,25 +203,7 @@ const AccountSettings: React.FC<AccountSettingsProps> = ({
|
|||||||
<Accordion
|
<Accordion
|
||||||
defaultValue={['profile', 'security', 'danger']}
|
defaultValue={['profile', 'security', 'danger']}
|
||||||
multiple
|
multiple
|
||||||
styles={(theme: any) => ({
|
styles={(theme) => getAccordionStyles(theme)}
|
||||||
control: {
|
|
||||||
paddingTop: theme.spacing.md,
|
|
||||||
paddingBottom: theme.spacing.md,
|
|
||||||
},
|
|
||||||
item: {
|
|
||||||
borderBottom: `1px solid ${
|
|
||||||
theme.colorScheme === 'dark'
|
|
||||||
? theme.colors.dark[4]
|
|
||||||
: theme.colors.gray[3]
|
|
||||||
}`,
|
|
||||||
'&[data-active]': {
|
|
||||||
backgroundColor:
|
|
||||||
theme.colorScheme === 'dark'
|
|
||||||
? theme.colors.dark[7]
|
|
||||||
: theme.colors.gray[0],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<Accordion.Item value="profile">
|
<Accordion.Item value="profile">
|
||||||
<AccordionControl>Profile</AccordionControl>
|
<AccordionControl>Profile</AccordionControl>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
type SettingsAction,
|
type SettingsAction,
|
||||||
SettingsActionType,
|
SettingsActionType,
|
||||||
} from '@/types/models';
|
} from '@/types/models';
|
||||||
|
import { getAccordionStyles } from '@/utils/themeStyles';
|
||||||
// State and reducer for workspace settings
|
// State and reducer for workspace settings
|
||||||
interface WorkspaceSettingsState {
|
interface WorkspaceSettingsState {
|
||||||
localSettings: Partial<Workspace>;
|
localSettings: Partial<Workspace>;
|
||||||
@@ -157,24 +158,8 @@ const WorkspaceSettings: React.FC = () => {
|
|||||||
<Accordion
|
<Accordion
|
||||||
defaultValue={['general', 'appearance', 'editor', 'git', 'danger']}
|
defaultValue={['general', 'appearance', 'editor', 'git', 'danger']}
|
||||||
multiple
|
multiple
|
||||||
styles={(theme: any) => ({
|
styles={(theme) => ({
|
||||||
control: {
|
...getAccordionStyles(theme),
|
||||||
paddingTop: theme.spacing.md,
|
|
||||||
paddingBottom: theme.spacing.md,
|
|
||||||
},
|
|
||||||
item: {
|
|
||||||
borderBottom: `1px solid ${
|
|
||||||
theme.colorScheme === 'dark'
|
|
||||||
? theme.colors.dark[4]
|
|
||||||
: theme.colors.gray[3]
|
|
||||||
}`,
|
|
||||||
'&[data-active]': {
|
|
||||||
backgroundColor:
|
|
||||||
theme.colorScheme === 'dark'
|
|
||||||
? theme.colors.dark[7]
|
|
||||||
: theme.colors.gray[0],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
chevron: {
|
chevron: {
|
||||||
'&[data-rotate]': {
|
'&[data-rotate]': {
|
||||||
transform: 'rotate(180deg)',
|
transform: 'rotate(180deg)',
|
||||||
|
|||||||
80
app/src/utils/themeStyles.ts
Normal file
80
app/src/utils/themeStyles.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import type { MantineTheme } from '@mantine/core';
|
||||||
|
|
||||||
|
// For type safety - this property exists on the MantineTheme but may not be in types
|
||||||
|
interface ThemeWithColorScheme extends MantineTheme {
|
||||||
|
colorScheme?: 'dark' | 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-safe hover style function for unstyledButton and similar components
|
||||||
|
export const getHoverStyle = (theme: MantineTheme) => ({
|
||||||
|
borderRadius: theme.radius.sm,
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor:
|
||||||
|
(theme as ThemeWithColorScheme).colorScheme === 'dark'
|
||||||
|
? theme.colors.dark[5]
|
||||||
|
: theme.colors.gray[0],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Type-safe color function for text or components that need conditional colors
|
||||||
|
export const getConditionalColor = (
|
||||||
|
theme: MantineTheme,
|
||||||
|
isSelected = false
|
||||||
|
) => {
|
||||||
|
if (isSelected) {
|
||||||
|
return (theme as ThemeWithColorScheme).colorScheme === 'dark'
|
||||||
|
? theme.colors.blue[2]
|
||||||
|
: theme.colors.blue[7];
|
||||||
|
}
|
||||||
|
return 'dimmed';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper for accordion styling
|
||||||
|
export const getAccordionStyles = (theme: MantineTheme) => ({
|
||||||
|
control: {
|
||||||
|
paddingTop: theme.spacing.md,
|
||||||
|
paddingBottom: theme.spacing.md,
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
borderBottom: `1px solid ${
|
||||||
|
(theme as ThemeWithColorScheme).colorScheme === 'dark'
|
||||||
|
? theme.colors.dark[4]
|
||||||
|
: theme.colors.gray[3]
|
||||||
|
}`,
|
||||||
|
'&[data-active]': {
|
||||||
|
backgroundColor:
|
||||||
|
(theme as ThemeWithColorScheme).colorScheme === 'dark'
|
||||||
|
? theme.colors.dark[7]
|
||||||
|
: theme.colors.gray[0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper for workspace paper styling
|
||||||
|
export const getWorkspacePaperStyle = (
|
||||||
|
theme: MantineTheme,
|
||||||
|
isSelected: boolean
|
||||||
|
) => ({
|
||||||
|
backgroundColor: isSelected
|
||||||
|
? theme.colors.blue[
|
||||||
|
(theme as ThemeWithColorScheme).colorScheme === 'dark' ? 8 : 1
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
|
borderColor: isSelected
|
||||||
|
? theme.colors.blue[
|
||||||
|
(theme as ThemeWithColorScheme).colorScheme === 'dark' ? 7 : 5
|
||||||
|
]
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper for text color based on theme and selection
|
||||||
|
export const getTextColor = (
|
||||||
|
theme: MantineTheme,
|
||||||
|
isSelected: boolean
|
||||||
|
): string | null => {
|
||||||
|
if (!isSelected) return null;
|
||||||
|
|
||||||
|
return (theme as ThemeWithColorScheme).colorScheme === 'dark'
|
||||||
|
? theme.colors.blue[0]
|
||||||
|
: theme.colors.blue[9];
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user