mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-16 02:06:21 +01:00
🚧 Work in progress for Mantine v5
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Group, Text, Title } from '@mantine/core';
|
||||
import { Group, Stack, Text, Title } from '@mantine/core';
|
||||
import dayjs from 'dayjs';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IconClock as Clock } from '@tabler/icons';
|
||||
@@ -34,7 +34,7 @@ export default function DateComponent(props: any) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Group p="sm" spacing="xs" direction="column">
|
||||
<Group p="sm" spacing="xs">
|
||||
<Title>{dayjs(date).format(formatString)}</Title>
|
||||
<Text size="xl">{dayjs(date).format('dddd, MMMM D')}</Text>
|
||||
</Group>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Button, Group, Modal, Title } from '@mantine/core';
|
||||
import { useBooleanToggle } from '@mantine/hooks';
|
||||
import { showNotification, updateNotification } from '@mantine/notifications';
|
||||
import {
|
||||
IconCheck,
|
||||
@@ -14,6 +13,7 @@ import axios from 'axios';
|
||||
import Dockerode from 'dockerode';
|
||||
import { tryMatchService } from '../../tools/addToHomarr';
|
||||
import { AddAppShelfItemForm } from '../../components/AppShelf/AddAppShelfItem';
|
||||
import { useState } from 'react';
|
||||
|
||||
function sendDockerCommand(
|
||||
action: string,
|
||||
@@ -60,7 +60,7 @@ export interface ContainerActionBarProps {
|
||||
}
|
||||
|
||||
export default function ContainerActionBar({ selected, reload }: ContainerActionBarProps) {
|
||||
const [opened, setOpened] = useBooleanToggle(false);
|
||||
const [opened, setOpened] = useState<boolean>(false);
|
||||
return (
|
||||
<Group>
|
||||
<Modal
|
||||
|
||||
@@ -20,7 +20,6 @@ export default function DockerMenuButton(props: any) {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [containers, setContainers] = useState<Docker.ContainerInfo[]>([]);
|
||||
const [selection, setSelection] = useState<Docker.ContainerInfo[]>([]);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { config } = useConfig();
|
||||
const moduleEnabled = config.modules?.[DockerModule.title]?.enabled ?? false;
|
||||
|
||||
@@ -32,14 +31,12 @@ export default function DockerMenuButton(props: any) {
|
||||
if (!moduleEnabled) {
|
||||
return;
|
||||
}
|
||||
setVisible(true);
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.get('/api/docker/containers')
|
||||
.then((res) => {
|
||||
setContainers(res.data);
|
||||
setSelection([]);
|
||||
setVisible(false);
|
||||
})
|
||||
.catch(() =>
|
||||
// Send an Error notification
|
||||
@@ -61,12 +58,14 @@ export default function DockerMenuButton(props: any) {
|
||||
if (containers.length < 1) return null;
|
||||
return (
|
||||
<>
|
||||
<Drawer opened={opened} onClose={() => setOpened(false)} padding="xl" size="full">
|
||||
<ContainerActionBar selected={selection} reload={reload} />
|
||||
<div style={{ position: 'relative' }}>
|
||||
<LoadingOverlay transitionDuration={500} visible={visible} />
|
||||
<DockerTable containers={containers} selection={selection} setSelection={setSelection} />
|
||||
</div>
|
||||
<Drawer
|
||||
opened={opened}
|
||||
onClose={() => setOpened(false)}
|
||||
padding="xl"
|
||||
size="full"
|
||||
title={<ContainerActionBar selected={selection} reload={reload} />}
|
||||
>
|
||||
<DockerTable containers={containers} selection={selection} setSelection={setSelection} />
|
||||
</Drawer>
|
||||
<Group position="center">
|
||||
<ActionIcon
|
||||
|
||||
@@ -101,7 +101,6 @@ export default function DockerTable({
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
<Table captionSide="bottom" highlightOnHover sx={{ minWidth: 800 }} verticalSpacing="sm">
|
||||
<caption>your docker containers</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: 40 }}>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ScrollArea,
|
||||
Center,
|
||||
Image,
|
||||
Stack,
|
||||
} from '@mantine/core';
|
||||
import { IconDownload as Download } from '@tabler/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -187,13 +188,8 @@ export default function DownloadComponent() {
|
||||
);
|
||||
});
|
||||
|
||||
const easteregg = (
|
||||
<Center style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Image fit="cover" height={300} src="https://danjohnvelasco.github.io/images/empty.png" />
|
||||
</Center>
|
||||
);
|
||||
return (
|
||||
<Group noWrap grow direction="column" mt="xl">
|
||||
<Stack mt="xl">
|
||||
<ScrollArea sx={{ height: 300 }}>
|
||||
{rows.length > 0 ? (
|
||||
<Table highlightOnHover>
|
||||
@@ -201,9 +197,15 @@ export default function DownloadComponent() {
|
||||
<tbody>{rows}</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
easteregg
|
||||
<Center style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Image
|
||||
fit="cover"
|
||||
height={300}
|
||||
src="https://danjohnvelasco.github.io/images/empty.png"
|
||||
/>
|
||||
</Center>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Text, Title, Group, useMantineTheme, Box, Card, ColorSwatch } from '@mantine/core';
|
||||
import { Text, Title, Group, useMantineTheme, Box, Card, ColorSwatch, Stack } from '@mantine/core';
|
||||
import { IconDownload as Download } from '@tabler/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
import axios from 'axios';
|
||||
@@ -78,13 +78,13 @@ export default function TotalDownloadsComponent() {
|
||||
|
||||
if (downloadServices.length === 0) {
|
||||
return (
|
||||
<Group direction="column">
|
||||
<Stack>
|
||||
<Title order={4}>No supported download clients found!</Title>
|
||||
<Group noWrap>
|
||||
<Text>Add a download service to view your current downloads...</Text>
|
||||
<AddItemShelfButton />
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,9 +101,9 @@ export default function TotalDownloadsComponent() {
|
||||
})) as Datum[];
|
||||
|
||||
return (
|
||||
<Group noWrap direction="column" grow>
|
||||
<Stack>
|
||||
<Title order={4}>Current download speed</Title>
|
||||
<Group direction="column">
|
||||
<Stack>
|
||||
<Group>
|
||||
<ColorSwatch size={12} color={theme.colors.green[5]} />
|
||||
<Text>Download: {humanFileSize(totalDownloadSpeed)}/s</Text>
|
||||
@@ -112,7 +112,7 @@ export default function TotalDownloadsComponent() {
|
||||
<ColorSwatch size={12} color={theme.colors.blue[5]} />
|
||||
<Text>Upload: {humanFileSize(totalUploadSpeed)}/s</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
<Box
|
||||
style={{
|
||||
height: 200,
|
||||
@@ -133,7 +133,7 @@ export default function TotalDownloadsComponent() {
|
||||
<Card p="sm" radius="md" withBorder>
|
||||
<Text size="md">{roundedSeconds} seconds ago</Text>
|
||||
<Card.Section p="sm">
|
||||
<Group direction="column">
|
||||
<Stack>
|
||||
<Group>
|
||||
<ColorSwatch size={10} color={theme.colors.green[5]} />
|
||||
<Text size="md">Download: {humanFileSize(Download)}</Text>
|
||||
@@ -142,7 +142,7 @@ export default function TotalDownloadsComponent() {
|
||||
<ColorSwatch size={10} color={theme.colors.blue[5]} />
|
||||
<Text size="md">Upload: {humanFileSize(Upload)}</Text>
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Card.Section>
|
||||
</Card>
|
||||
);
|
||||
@@ -181,6 +181,6 @@ export default function TotalDownloadsComponent() {
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Group,
|
||||
@@ -8,6 +10,9 @@ import {
|
||||
TextInput,
|
||||
useMantineColorScheme,
|
||||
} from '@mantine/core';
|
||||
import { useHover } from '@mantine/hooks';
|
||||
import { IconAdjustments } from '@tabler/icons';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useConfig } from '../tools/state';
|
||||
import { IModule } from './ModuleTypes';
|
||||
|
||||
@@ -142,6 +147,8 @@ export function ModuleWrapper(props: any) {
|
||||
const enabledModules = config.modules ?? {};
|
||||
// Remove 'Module' from enabled modules titles
|
||||
const isShown = enabledModules[module.title]?.enabled ?? false;
|
||||
//TODO: fix the hover problem
|
||||
const { hovered, ref } = useHover();
|
||||
|
||||
if (!isShown) {
|
||||
return null;
|
||||
@@ -150,6 +157,8 @@ export function ModuleWrapper(props: any) {
|
||||
return (
|
||||
<Card
|
||||
{...props}
|
||||
key={module.title}
|
||||
ref={ref}
|
||||
hidden={!isShown}
|
||||
withBorder
|
||||
radius="lg"
|
||||
@@ -161,47 +170,61 @@ export function ModuleWrapper(props: any) {
|
||||
${(config.settings.appOpacity || 100) / 100}`,
|
||||
}}
|
||||
>
|
||||
<ModuleMenu
|
||||
module={module}
|
||||
styles={{
|
||||
root: {
|
||||
position: 'absolute',
|
||||
top: 12,
|
||||
right: 12,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<module.component />
|
||||
<Group position="apart">
|
||||
<ModuleMenu module={module} hovered={hovered} />
|
||||
<module.component />
|
||||
</Group>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export function ModuleMenu(props: any) {
|
||||
const { module, styles } = props;
|
||||
const { module, styles, hovered } = props;
|
||||
const items: JSX.Element[] = getItems(module);
|
||||
return (
|
||||
<>
|
||||
{module.options && (
|
||||
<Menu
|
||||
size="lg"
|
||||
withinPortal
|
||||
width="lg"
|
||||
shadow="xl"
|
||||
withArrow
|
||||
closeOnItemClick={false}
|
||||
radius="md"
|
||||
position="left"
|
||||
styles={{
|
||||
root: {
|
||||
...props?.styles?.root,
|
||||
},
|
||||
body: {
|
||||
dropdown: {
|
||||
// Add shadow and elevation to the body
|
||||
boxShadow: '0 0 14px 14px rgba(0, 0, 0, 0.05)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Menu.Label>Settings</Menu.Label>
|
||||
{items.map((item) => (
|
||||
<Menu.Item key={item.key}>{item}</Menu.Item>
|
||||
))}
|
||||
<Menu.Target>
|
||||
<Box
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 12,
|
||||
right: 12,
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
animate={{
|
||||
//TODO: fix the hover problem
|
||||
opacity: hovered ? 1 : 1,
|
||||
}}
|
||||
>
|
||||
<ActionIcon>
|
||||
<IconAdjustments />
|
||||
</ActionIcon>
|
||||
</motion.div>
|
||||
</Box>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Label>Settings</Menu.Label>
|
||||
{items.map((item) => (
|
||||
<Menu.Item key={item.key}>{item}</Menu.Item>
|
||||
))}
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Kbd, createStyles, Autocomplete } from '@mantine/core';
|
||||
import { useDebouncedValue, useForm, useHotkeys } from '@mantine/hooks';
|
||||
import { useDebouncedValue, useHotkeys } from '@mantine/hooks';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
IconSearch as Search,
|
||||
|
||||
Reference in New Issue
Block a user