mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-01 02:56:04 +01:00
Rework appshell and include calendar to the main page
This commit is contained in:
@@ -103,30 +103,34 @@ export default function AddItemShelfItem(props: any) {
|
|||||||
</Group>
|
</Group>
|
||||||
</form>
|
</form>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Grid.Col span={4} lg={2} sm={3}>
|
<AspectRatio
|
||||||
<AspectRatio ratio={4 / 3}>
|
style={{
|
||||||
<Box
|
minHeight: 120,
|
||||||
sx={{
|
minWidth: 120,
|
||||||
|
}}
|
||||||
|
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': {
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
|
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
||||||
textAlign: 'center',
|
},
|
||||||
padding: theme.spacing.xl,
|
}}
|
||||||
borderRadius: theme.radius.md,
|
>
|
||||||
'&:hover': {
|
<Group direction="column" position="center">
|
||||||
backgroundColor:
|
<motion.div whileHover={{ scale: 1.2 }}>
|
||||||
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
<Apps style={{ cursor: 'pointer' }} onClick={() => setOpened(true)} size={60} />
|
||||||
},
|
</motion.div>
|
||||||
}}
|
<Text>Add Service</Text>
|
||||||
>
|
</Group>
|
||||||
<Group direction="column" position="center">
|
</Box>
|
||||||
<motion.div whileHover={{ scale: 1.2 }}>
|
</AspectRatio>
|
||||||
<Apps style={{ cursor: 'pointer' }} onClick={() => setOpened(true)} size={60} />
|
|
||||||
</motion.div>
|
|
||||||
<Text>Add Service</Text>
|
|
||||||
</Group>
|
|
||||||
</Box>
|
|
||||||
</AspectRatio>
|
|
||||||
</Grid.Col>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import {
|
|||||||
AspectRatio,
|
AspectRatio,
|
||||||
createStyles,
|
createStyles,
|
||||||
Center,
|
Center,
|
||||||
|
Container,
|
||||||
|
SimpleGrid,
|
||||||
|
Space,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { AlertCircle, Cross, X } from 'tabler-icons-react';
|
import { AlertCircle, Cross, X } from 'tabler-icons-react';
|
||||||
@@ -24,7 +27,9 @@ const useStyles = createStyles((theme) => ({
|
|||||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
padding: theme.spacing.xl,
|
padding: theme.spacing.xl,
|
||||||
borderRadius: theme.radius.md,
|
borderRadius: theme.radius.sm,
|
||||||
|
width: 200,
|
||||||
|
height: 180,
|
||||||
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[2],
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[2],
|
||||||
@@ -53,39 +58,42 @@ const AppShelf = (props: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid m="xl" gutter="xl">
|
<SimpleGrid m="xl" cols={4} spacing="xl">
|
||||||
{config.services
|
{config.services.map((service, i) => (
|
||||||
? config.services.map((service, i) => (
|
<motion.div
|
||||||
<Grid.Col lg={2} sm={3} key={i}>
|
onHoverStart={(e) => {
|
||||||
<motion.div
|
setHovering(service.name);
|
||||||
onHoverStart={(e) => {
|
}}
|
||||||
setHovering(service.name);
|
onHoverEnd={(e) => {
|
||||||
}}
|
setHovering('none');
|
||||||
onHoverEnd={(e) => {
|
}}
|
||||||
setHovering('none');
|
>
|
||||||
}}
|
<Box className={classes.main}>
|
||||||
>
|
<Group position="center">
|
||||||
<AspectRatio ratio={4 / 3}>
|
<Space />
|
||||||
<Box className={classes.main}>
|
<Text>{service.name}</Text>
|
||||||
<motion.div animate={{ opacity: hovering == service.name ? 1 : 0 }}>
|
<motion.div animate={{ opacity: hovering == service.name ? 1 : 0 }}>
|
||||||
<AppShelfMenu removeitem={removeService} name={service.name} />
|
<AppShelfMenu removeitem={removeService} name={service.name} />
|
||||||
</motion.div>
|
|
||||||
<Group direction="column" position="center">
|
|
||||||
<Anchor href={service.url} target="_blank">
|
|
||||||
<motion.div whileHover={{ scale: 1.2 }}>
|
|
||||||
<Image style={{ maxWidth: 60 }} src={service.icon} alt={service.name} />
|
|
||||||
</motion.div>
|
|
||||||
</Anchor>
|
|
||||||
<Text>{service.name}</Text>
|
|
||||||
</Group>
|
|
||||||
</Box>
|
|
||||||
</AspectRatio>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Grid.Col>
|
</Group>
|
||||||
))
|
<Group direction="column" position="center">
|
||||||
: null}
|
<Anchor href={service.url} target="_blank">
|
||||||
<AddItemShelfItem additem={addService} />
|
<motion.div whileHover={{ scale: 1.2 }}>
|
||||||
</Grid>
|
<Image
|
||||||
|
style={{
|
||||||
|
maxWidth: 100,
|
||||||
|
}}
|
||||||
|
fit="cover"
|
||||||
|
src={service.icon}
|
||||||
|
alt={service.name}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</Anchor>
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Check, Edit, Trash } from 'tabler-icons-react';
|
|||||||
export default function AppShelfMenu(props: any) {
|
export default function AppShelfMenu(props: any) {
|
||||||
const { name, removeitem: removeItem } = props;
|
const { name, removeitem: removeItem } = props;
|
||||||
return (
|
return (
|
||||||
<Menu sx={{ position: 'absolute', top: 3, right: 3 }}>
|
<Menu position='right'>
|
||||||
<Menu.Label>Settings</Menu.Label>
|
<Menu.Label>Settings</Menu.Label>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
useMantineTheme,
|
useMantineTheme,
|
||||||
Center,
|
Center,
|
||||||
Popover,
|
Popover,
|
||||||
|
Box,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useForm } from '@mantine/hooks';
|
import { useForm } from '@mantine/hooks';
|
||||||
import { showNotification } from '@mantine/notifications';
|
import { showNotification } from '@mantine/notifications';
|
||||||
@@ -39,65 +40,70 @@ export default function SearchBar(props: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<Box
|
||||||
onChange={() => {
|
style={{
|
||||||
// If querry contains !yt or !t add "Searching on YouTube" or "Searching torrent"
|
width: '100%',
|
||||||
const querry = form.values.querry.trim();
|
|
||||||
const isYoutube = querry.startsWith('!yt');
|
|
||||||
const isTorrent = querry.startsWith('!t');
|
|
||||||
if (isYoutube) {
|
|
||||||
setIcon(<BrandYoutube />);
|
|
||||||
} else if (isTorrent) {
|
|
||||||
setIcon(<Download />);
|
|
||||||
} else {
|
|
||||||
setIcon(<Search />);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
onSubmit={form.onSubmit((values) => {
|
|
||||||
// Find if querry is prefixed by !yt or !t
|
|
||||||
const querry = values.querry.trim();
|
|
||||||
const isYoutube = querry.startsWith('!yt');
|
|
||||||
const isTorrent = querry.startsWith('!t');
|
|
||||||
if (isYoutube) {
|
|
||||||
window.open(`https://www.youtube.com/results?search_query=${querry.substring(3)}`);
|
|
||||||
} else if (isTorrent) {
|
|
||||||
window.open(`https://thepiratebay.org/search.php?q=${querry.substring(3)}`);
|
|
||||||
} else {
|
|
||||||
window.open(`${querryUrl}${values.querry}`);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<Popover
|
<form
|
||||||
opened={opened}
|
onChange={() => {
|
||||||
style={{
|
// If querry contains !yt or !t add "Searching on YouTube" or "Searching torrent"
|
||||||
width: '100%',
|
const querry = form.values.querry.trim();
|
||||||
|
const isYoutube = querry.startsWith('!yt');
|
||||||
|
const isTorrent = querry.startsWith('!t');
|
||||||
|
if (isYoutube) {
|
||||||
|
setIcon(<BrandYoutube size={22} />);
|
||||||
|
} else if (isTorrent) {
|
||||||
|
setIcon(<Download size={22} />);
|
||||||
|
} else {
|
||||||
|
setIcon(<Search size={22} />);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
position="bottom"
|
onSubmit={form.onSubmit((values) => {
|
||||||
placement="start"
|
// Find if querry is prefixed by !yt or !t
|
||||||
withArrow
|
const querry = values.querry.trim();
|
||||||
trapFocus={false}
|
const isYoutube = querry.startsWith('!yt');
|
||||||
transition="pop-top-left"
|
const isTorrent = querry.startsWith('!t');
|
||||||
onFocusCapture={() => setOpened(true)}
|
if (isYoutube) {
|
||||||
onBlurCapture={() => setOpened(false)}
|
window.open(`https://www.youtube.com/results?search_query=${querry.substring(3)}`);
|
||||||
target={
|
} else if (isTorrent) {
|
||||||
<TextInput
|
window.open(`https://thepiratebay.org/search.php?q=${querry.substring(3)}`);
|
||||||
variant="filled"
|
} else {
|
||||||
color="blue"
|
window.open(`${querryUrl}${values.querry}`);
|
||||||
icon={<Search size={18} />}
|
}
|
||||||
radius="md"
|
})}
|
||||||
rightSection={icon}
|
|
||||||
size="md"
|
|
||||||
placeholder="Search the web"
|
|
||||||
{...props}
|
|
||||||
{...form.getInputProps('querry')}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Text>
|
<Popover
|
||||||
tip: You can prefix your querry with <b>!yt</b> or <b>!t</b> to research on youtube or for
|
opened={opened}
|
||||||
a torrent
|
style={{
|
||||||
</Text>
|
width: '100%',
|
||||||
</Popover>
|
}}
|
||||||
</form>
|
position="bottom"
|
||||||
|
placement="start"
|
||||||
|
withArrow
|
||||||
|
trapFocus={false}
|
||||||
|
transition="pop-top-left"
|
||||||
|
onFocusCapture={() => setOpened(true)}
|
||||||
|
onBlurCapture={() => setOpened(false)}
|
||||||
|
target={
|
||||||
|
<TextInput
|
||||||
|
variant="filled"
|
||||||
|
color="blue"
|
||||||
|
icon={icon}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
placeholder="Search the web"
|
||||||
|
{...props}
|
||||||
|
{...form.getInputProps('querry')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
tip: You can prefix your querry with <b>!yt</b> or <b>!t</b> to research on youtube or
|
||||||
|
for a torrent
|
||||||
|
</Text>
|
||||||
|
</Popover>
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Group, Indicator, Popover, Box, Container, Text, Avatar } from '@mantine/core';
|
import { Group, Indicator, Popover, Box, Container, Text, Avatar, ActionIcon } from '@mantine/core';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Calendar } from '@mantine/dates';
|
import { Calendar } from '@mantine/dates';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -27,7 +27,6 @@ export default function CalendarComponent(props: any) {
|
|||||||
const [opened, setOpened] = useState(false);
|
const [opened, setOpened] = useState(false);
|
||||||
// const [medias, setMedias] = useState();
|
// const [medias, setMedias] = useState();
|
||||||
const dates = medias.map((media) => media.inCinemas);
|
const dates = medias.map((media) => media.inCinemas);
|
||||||
const [value, setValue] = useState(null);
|
|
||||||
const parsedDates = dates.map((date) => dayjs(date));
|
const parsedDates = dates.map((date) => dayjs(date));
|
||||||
console.log(parsedDates);
|
console.log(parsedDates);
|
||||||
|
|
||||||
@@ -64,10 +63,7 @@ export default function CalendarComponent(props: any) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Calendar
|
<Calendar
|
||||||
value={value}
|
onChange={(day: any) => {}}
|
||||||
onChange={(day: any) => {
|
|
||||||
setValue(day);
|
|
||||||
}}
|
|
||||||
renderDay={(renderdate) => <DayComponent renderdate={renderdate} parsedDates={parsedDates} />}
|
renderDay={(renderdate) => <DayComponent renderdate={renderdate} parsedDates={parsedDates} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -82,13 +78,13 @@ function DayComponent(props: any) {
|
|||||||
|
|
||||||
if (match > -1) {
|
if (match > -1) {
|
||||||
return (
|
return (
|
||||||
<Avatar
|
<ActionIcon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpened(true);
|
setOpened(true);
|
||||||
console.log();
|
console.log();
|
||||||
}}
|
}}
|
||||||
radius="xl"
|
|
||||||
color="teal"
|
color="teal"
|
||||||
|
variant="light"
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
position="right"
|
position="right"
|
||||||
@@ -98,7 +94,7 @@ function DayComponent(props: any) {
|
|||||||
target={day}
|
target={day}
|
||||||
children={<MediaDisplay media={medias[match]} />}
|
children={<MediaDisplay media={medias[match]} />}
|
||||||
/>
|
/>
|
||||||
</Avatar>
|
</ActionIcon>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <div>{day}</div>;
|
return <div>{day}</div>;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Notification } from '@mantine/core';
|
import { Group, Notification } from '@mantine/core';
|
||||||
import AppShelf from '../components/AppShelf/AppShelf';
|
import AppShelf from '../components/AppShelf/AppShelf';
|
||||||
|
import CalendarComponent from '../components/calendar/CalendarComponent';
|
||||||
import LoadConfigComponent from '../components/Config/LoadConfig';
|
import LoadConfigComponent from '../components/Config/LoadConfig';
|
||||||
import SearchBar from '../components/SearchBar/SearchBar';
|
import SearchBar from '../components/SearchBar/SearchBar';
|
||||||
|
|
||||||
@@ -7,7 +8,10 @@ export default function HomePage() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
<AppShelf />
|
<Group align={"start"} position="apart" noWrap>
|
||||||
|
<AppShelf />
|
||||||
|
<CalendarComponent />
|
||||||
|
</Group>
|
||||||
<LoadConfigComponent />
|
<LoadConfigComponent />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user