mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-14 17:26:26 +01:00
Improve UI
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
Box,
|
||||
Text,
|
||||
Grid,
|
||||
Card,
|
||||
} from '@mantine/core';
|
||||
import { useForm } from '@mantine/hooks';
|
||||
import { motion } from 'framer-motion';
|
||||
@@ -30,9 +31,8 @@ export default function AddItemShelfItem(props: any) {
|
||||
name: '',
|
||||
icon: '',
|
||||
url: '',
|
||||
apiKey: undefined as unknown as string,
|
||||
},
|
||||
|
||||
validationRules: {},
|
||||
});
|
||||
return (
|
||||
<>
|
||||
@@ -85,7 +85,7 @@ export default function AddItemShelfItem(props: any) {
|
||||
placeholder="http://localhost:8989"
|
||||
value={form.values.url}
|
||||
onChange={(event) => form.setFieldValue('url', event.currentTarget.value)}
|
||||
error={form.errors.icon && 'Icon url is invalid'}
|
||||
error={form.errors.url && 'Service url is invalid'}
|
||||
/>
|
||||
<Select
|
||||
label="Select the type of service (used for API calls)"
|
||||
@@ -97,6 +97,16 @@ export default function AddItemShelfItem(props: any) {
|
||||
onChange={(value) => form.setFieldValue('type', value ?? 'Other')}
|
||||
data={ServiceTypeList}
|
||||
/>
|
||||
{(form.values.type === 'Sonarr' || form.values.type === 'Radarr') && (
|
||||
<TextInput
|
||||
required
|
||||
label="API key"
|
||||
placeholder="Your API key"
|
||||
value={form.values.apiKey}
|
||||
onChange={(event) => form.setFieldValue('apiKey', event.currentTarget.value)}
|
||||
error={form.errors.apiKey && 'Invalid API key'}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
|
||||
<Group grow position="center" mt="xl">
|
||||
@@ -111,18 +121,15 @@ export default function AddItemShelfItem(props: any) {
|
||||
}}
|
||||
ratio={4 / 3}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
|
||||
textAlign: 'center',
|
||||
padding: theme.spacing.xl,
|
||||
borderRadius: theme.radius.md,
|
||||
'&:hover': {
|
||||
<Card
|
||||
style={{
|
||||
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
||||
},
|
||||
width: 200,
|
||||
height: 180,
|
||||
}}
|
||||
radius="md"
|
||||
>
|
||||
<Group direction="column" position="center">
|
||||
<motion.div whileHover={{ scale: 1.2 }}>
|
||||
@@ -130,7 +137,7 @@ export default function AddItemShelfItem(props: any) {
|
||||
</motion.div>
|
||||
<Text>Add Service</Text>
|
||||
</Group>
|
||||
</Box>
|
||||
</Card>
|
||||
</AspectRatio>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import AppShelf from './AppShelf';
|
||||
import AppShelf, { AppShelfItem } from './AppShelf';
|
||||
|
||||
export default {
|
||||
title: 'Item Shelf',
|
||||
component: AppShelf,
|
||||
args: {
|
||||
service: {
|
||||
name: 'qBittorrent',
|
||||
url: 'http://',
|
||||
icon: 'https://cdn.jsdelivr.net/gh/IceWhaleTech/CasaOS-AppStore@main/Apps/qBittorrent/icon.png',
|
||||
type: 'qBittorrent',
|
||||
apiKey: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Default = (args: any) => <AppShelf {...args} />;
|
||||
export const One = (args: any) => <AppShelfItem {...args} />;
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Grid,
|
||||
Group,
|
||||
Text,
|
||||
Image,
|
||||
Anchor,
|
||||
Box,
|
||||
AspectRatio,
|
||||
@@ -13,6 +12,10 @@ import {
|
||||
Container,
|
||||
SimpleGrid,
|
||||
Space,
|
||||
Card,
|
||||
useMantineTheme,
|
||||
Image,
|
||||
Badge,
|
||||
} from '@mantine/core';
|
||||
import { showNotification } from '@mantine/notifications';
|
||||
import { AlertCircle, Cross, X } from 'tabler-icons-react';
|
||||
@@ -20,27 +23,19 @@ import AppShelfMenu from './AppShelfMenu';
|
||||
import AddItemShelfItem from './AddAppShelfItem';
|
||||
import { useConfig } from '../../tools/state';
|
||||
import { pingQbittorrent } from '../../tools/api';
|
||||
import { Config } from '../../tools/types';
|
||||
import { Config, serviceItem } from '../../tools/types';
|
||||
import { SettingsMenuButton } from '../Settings/SettingsMenu';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
main: {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
||||
textAlign: 'center',
|
||||
padding: theme.spacing.xl,
|
||||
borderRadius: theme.radius.sm,
|
||||
width: 200,
|
||||
height: 180,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[2],
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const AppShelf = (props: any) => {
|
||||
const { config, addService, removeService, setConfig } = useConfig();
|
||||
const { classes } = useStyles();
|
||||
const [hovering, setHovering] = useState('none');
|
||||
|
||||
/* A hook that is used to load the config from local storage. */
|
||||
useEffect(() => {
|
||||
@@ -60,42 +55,73 @@ const AppShelf = (props: any) => {
|
||||
return (
|
||||
<SimpleGrid m="xl" cols={4} spacing="xl">
|
||||
{config.services.map((service, i) => (
|
||||
<motion.div
|
||||
onHoverStart={(e) => {
|
||||
setHovering(service.name);
|
||||
}}
|
||||
onHoverEnd={(e) => {
|
||||
setHovering('none');
|
||||
}}
|
||||
>
|
||||
<Box className={classes.main}>
|
||||
<Group position="center">
|
||||
<Space />
|
||||
<Text>{service.name}</Text>
|
||||
<motion.div animate={{ opacity: hovering == service.name ? 1 : 0 }}>
|
||||
<AppShelfMenu removeitem={removeService} name={service.name} />
|
||||
</motion.div>
|
||||
</Group>
|
||||
<Group direction="column" position="center">
|
||||
<Anchor href={service.url} target="_blank">
|
||||
<motion.div whileHover={{ scale: 1.2 }}>
|
||||
<Image
|
||||
style={{
|
||||
maxWidth: 80,
|
||||
}}
|
||||
fit="cover"
|
||||
src={service.icon}
|
||||
alt={service.name}
|
||||
/>
|
||||
</motion.div>
|
||||
</Anchor>
|
||||
</Group>
|
||||
</Box>
|
||||
</motion.div>
|
||||
<AppShelfItem service={service} />
|
||||
))}
|
||||
<AddItemShelfItem/>
|
||||
<AddItemShelfItem />
|
||||
</SimpleGrid>
|
||||
);
|
||||
};
|
||||
|
||||
export function AppShelfItem(props: any) {
|
||||
const { service }: { service: serviceItem } = props;
|
||||
const theme = useMantineTheme();
|
||||
const { removeService } = useConfig();
|
||||
const { classes } = useStyles();
|
||||
const [hovering, setHovering] = useState(false);
|
||||
return (
|
||||
<motion.div
|
||||
onHoverStart={(e) => {
|
||||
setHovering(true);
|
||||
}}
|
||||
onHoverEnd={(e) => {
|
||||
setHovering(false);
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
className={classes.main}
|
||||
style={{
|
||||
boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)',
|
||||
}}
|
||||
radius={'md'}
|
||||
>
|
||||
<motion.div
|
||||
animate={{
|
||||
opacity: hovering ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
<AppShelfMenu name={service.name} removeitem={removeService} />
|
||||
</motion.div>
|
||||
<Card.Section>
|
||||
<Center>
|
||||
<Text mt={'sm'} weight={500}>
|
||||
{service.name}
|
||||
</Text>
|
||||
</Center>
|
||||
</Card.Section>
|
||||
<Card.Section>
|
||||
<AspectRatio ratio={5 / 3} m="xl">
|
||||
<motion.i
|
||||
whileHover={{
|
||||
cursor: 'pointer',
|
||||
scale: 1.1,
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
onClick={() => {
|
||||
window.open(service.url);
|
||||
}}
|
||||
style={{
|
||||
maxWidth: '50%',
|
||||
marginBottom: 10,
|
||||
}}
|
||||
src={service.icon}
|
||||
/>
|
||||
</motion.i>
|
||||
</AspectRatio>
|
||||
</Card.Section>
|
||||
</Card>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AppShelf;
|
||||
|
||||
@@ -5,7 +5,11 @@ import { Check, Edit, Trash } from 'tabler-icons-react';
|
||||
export default function AppShelfMenu(props: any) {
|
||||
const { name, removeitem: removeItem } = props;
|
||||
return (
|
||||
<Menu position='right'>
|
||||
<Menu style={{
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
right: 10,
|
||||
}}>
|
||||
<Menu.Label>Settings</Menu.Label>
|
||||
<Menu.Item
|
||||
color="primary"
|
||||
|
||||
@@ -7,6 +7,9 @@ import {
|
||||
Burger,
|
||||
Paper,
|
||||
Transition,
|
||||
Aside,
|
||||
Drawer,
|
||||
Center,
|
||||
} from '@mantine/core';
|
||||
import { useBooleanToggle } from '@mantine/hooks';
|
||||
import { NextLink } from '@mantine/next';
|
||||
@@ -14,6 +17,7 @@ import { Logo } from './Logo';
|
||||
import { ColorSchemeToggle } from '../ColorSchemeToggle/ColorSchemeToggle';
|
||||
import SaveConfigComponent from '../Config/SaveConfig';
|
||||
import { SettingsMenuButton } from '../Settings/SettingsMenu';
|
||||
import CalendarComponent from '../calendar/CalendarComponent';
|
||||
|
||||
const HEADER_HEIGHT = 60;
|
||||
|
||||
@@ -34,7 +38,7 @@ const useStyles = createStyles((theme) => ({
|
||||
borderTopWidth: 0,
|
||||
overflow: 'hidden',
|
||||
|
||||
[theme.fn.largerThan('sm')]: {
|
||||
[theme.fn.largerThan('md')]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
@@ -47,13 +51,13 @@ const useStyles = createStyles((theme) => ({
|
||||
},
|
||||
|
||||
links: {
|
||||
[theme.fn.smallerThan('sm')]: {
|
||||
[theme.fn.smallerThan('md')]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
burger: {
|
||||
[theme.fn.largerThan('sm')]: {
|
||||
[theme.fn.largerThan('md')]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
@@ -127,6 +131,7 @@ export function Header({ links }: HeaderResponsiveProps) {
|
||||
<Group spacing={5} className={classes.links}>
|
||||
{items}
|
||||
</Group>
|
||||
<Group>
|
||||
<SettingsMenuButton />
|
||||
|
||||
<Burger
|
||||
@@ -135,14 +140,19 @@ export function Header({ links }: HeaderResponsiveProps) {
|
||||
className={classes.burger}
|
||||
size="sm"
|
||||
/>
|
||||
</Group>
|
||||
|
||||
<Transition transition="pop-top-right" duration={200} mounted={opened}>
|
||||
{(styles) => (
|
||||
<Paper className={classes.dropdown} withBorder style={{ zIndex: 99 }}>
|
||||
{items}
|
||||
</Paper>
|
||||
)}
|
||||
</Transition>
|
||||
<Drawer
|
||||
opened={opened}
|
||||
overlayOpacity={0.55}
|
||||
overlayBlur={3}
|
||||
onClose={() => toggleOpened()}
|
||||
position="right"
|
||||
>
|
||||
<Center>
|
||||
<CalendarComponent />
|
||||
</Center>
|
||||
</Drawer>
|
||||
</Container>
|
||||
</Head>
|
||||
);
|
||||
|
||||
@@ -15,15 +15,13 @@ export default function Layout({ children, style }: any) {
|
||||
const { classes, cx } = useStyles();
|
||||
return (
|
||||
<AppShell
|
||||
|
||||
aside={
|
||||
<Aside
|
||||
height={'auto'}
|
||||
hiddenBreakpoint="md"
|
||||
hidden
|
||||
width={{
|
||||
xs: 'auto',
|
||||
md: 'auto',
|
||||
lg: 'auto',
|
||||
xl: 'auto',
|
||||
base: 'auto',
|
||||
}}
|
||||
>
|
||||
<CalendarComponent />
|
||||
|
||||
Reference in New Issue
Block a user