️ Work on responsiveness for the AppShelf

Fixes #1, Fixes #42, Fixes #82, Fixes #85
This commit is contained in:
Aj - Thomas
2022-05-14 21:45:54 +02:00
committed by GitHub
11 changed files with 143 additions and 121 deletions

View File

@@ -18,7 +18,7 @@ env:
# Use docker.io for Docker Hub if empty # Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io REGISTRY: ghcr.io
# github.repository as <account>/<repo> # github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }} IMAGE_NAME: ${{ github.repository }}-test
jobs: jobs:
# Push image to GitHub Packages. # Push image to GitHub Packages.

View File

@@ -10,6 +10,8 @@ import {
Text, Text,
Card, Card,
LoadingOverlay, LoadingOverlay,
ActionIcon,
Tooltip,
} from '@mantine/core'; } from '@mantine/core';
import { useForm } from '@mantine/form'; import { useForm } from '@mantine/form';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
@@ -19,13 +21,42 @@ import { useConfig } from '../../tools/state';
import { ServiceTypeList } from '../../tools/types'; import { ServiceTypeList } from '../../tools/types';
import { AppShelfItemWrapper } from './AppShelfItemWrapper'; import { AppShelfItemWrapper } from './AppShelfItemWrapper';
export function AddItemShelfButton(props: any) {
const [opened, setOpened] = useState(false);
return (
<>
<Modal
size="xl"
radius="md"
opened={props.opened || opened}
onClose={() => setOpened(false)}
title="Add a service"
>
<AddAppShelfItemForm setOpened={setOpened} />
</Modal>
<ActionIcon
variant="default"
radius="md"
size="xl"
color="blue"
style={props.style}
onClick={() => setOpened(true)}
>
<Tooltip label="Add a service">
<Apps />
</Tooltip>
</ActionIcon>
</>
);
}
export default function AddItemShelfItem(props: any) { export default function AddItemShelfItem(props: any) {
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
return ( return (
<> <>
<Modal <Modal
size="xl" size="xl"
radius="lg" radius="md"
opened={props.opened || opened} opened={props.opened || opened}
onClose={() => setOpened(false)} onClose={() => setOpened(false)}
title="Add a service" title="Add a service"

View File

@@ -1,3 +1,4 @@
import { SimpleGrid } from '@mantine/core';
import AppShelf, { AppShelfItem } from './AppShelf'; import AppShelf, { AppShelfItem } from './AppShelf';
export default { export default {
@@ -16,3 +17,10 @@ export default {
export const Default = (args: any) => <AppShelf {...args} />; export const Default = (args: any) => <AppShelf {...args} />;
export const One = (args: any) => <AppShelfItem {...args} />; export const One = (args: any) => <AppShelfItem {...args} />;
export const Ten = (args: any) => (
<SimpleGrid>
{Array.from(Array(10)).map((_, i) => (
<AppShelfItem {...args} key={i} />
))}
</SimpleGrid>
);

View File

@@ -1,21 +1,29 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { Text, AspectRatio, SimpleGrid, Card, Image, Group, Space } from '@mantine/core'; import { Text, AspectRatio, SimpleGrid, Card, Image, useMantineTheme } from '@mantine/core';
import { useConfig } from '../../tools/state'; import { useConfig } from '../../tools/state';
import { serviceItem } from '../../tools/types'; import { serviceItem } from '../../tools/types';
import AddItemShelfItem from './AddAppShelfItem';
import { AppShelfItemWrapper } from './AppShelfItemWrapper';
import AppShelfMenu from './AppShelfMenu'; import AppShelfMenu from './AppShelfMenu';
const AppShelf = () => { const AppShelf = () => {
const { config } = useConfig(); const { config } = useConfig();
return ( return (
<SimpleGrid m="xl" cols={5} spacing="xl"> <SimpleGrid
cols={7}
spacing="xl"
breakpoints={[
{ maxWidth: 2400, cols: 6, spacing: 'xl' },
{ maxWidth: 1800, cols: 5, spacing: 'xl' },
{ maxWidth: 1500, cols: 4, spacing: 'lg' },
{ maxWidth: 800, cols: 3, spacing: 'md' },
{ maxWidth: 400, cols: 3, spacing: 'sm' },
{ maxWidth: 400, cols: 2, spacing: 'sm' },
]}
>
{config.services.map((service) => ( {config.services.map((service) => (
<AppShelfItem key={service.name} service={service} /> <AppShelfItem key={service.name} service={service} />
))} ))}
<AddItemShelfItem />
</SimpleGrid> </SimpleGrid>
); );
}; };
@@ -23,6 +31,7 @@ const AppShelf = () => {
export function AppShelfItem(props: any) { export function AppShelfItem(props: any) {
const { service }: { service: serviceItem } = props; const { service }: { service: serviceItem } = props;
const [hovering, setHovering] = useState(false); const [hovering, setHovering] = useState(false);
const theme = useMantineTheme();
return ( return (
<motion.div <motion.div
key={service.name} key={service.name}
@@ -33,39 +42,41 @@ export function AppShelfItem(props: any) {
setHovering(false); setHovering(false);
}} }}
> >
<AppShelfItemWrapper hovering={hovering}> <Card
style={{
boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)',
backgroundColor:
theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[1],
}}
radius="md"
>
<Card.Section> <Card.Section>
<Group position="apart" mx="lg"> <Text mt="sm" align="center" lineClamp={1} weight={500}>
<Space /> {service.name}
<Text </Text>
// TODO: #1 Remove this hack to get the text to be centered. <motion.div
ml={15} style={{
style={{ position: 'absolute',
alignSelf: 'center', top: 5,
alignContent: 'center', right: 5,
alignItems: 'center', alignSelf: 'flex-end',
justifyContent: 'center', }}
justifyItems: 'center', animate={{
}} opacity: hovering ? 1 : 0,
mt="sm" }}
weight={500} >
> <AppShelfMenu service={service} />
{service.name} </motion.div>
</Text>
<motion.div
style={{
alignSelf: 'flex-end',
}}
animate={{
opacity: hovering ? 1 : 0,
}}
>
<AppShelfMenu service={service} />
</motion.div>
</Group>
</Card.Section> </Card.Section>
<Card.Section> <Card.Section>
<AspectRatio ratio={5 / 3} m="xl"> <AspectRatio
ratio={3 / 5}
m="xl"
style={{
width: 150,
height: 90,
}}
>
<motion.i <motion.i
whileHover={{ whileHover={{
cursor: 'pointer', cursor: 'pointer',
@@ -73,19 +84,19 @@ export function AppShelfItem(props: any) {
}} }}
> >
<Image <Image
style={{
maxWidth: 80,
}}
fit="contain"
onClick={() => { onClick={() => {
window.open(service.url); window.open(service.url);
}} }}
style={{
maxWidth: '50%',
marginBottom: 10,
}}
src={service.icon} src={service.icon}
/> />
</motion.i> </motion.i>
</AspectRatio> </AspectRatio>
</Card.Section> </Card.Section>
</AppShelfItemWrapper> </Card>
</motion.div> </motion.div>
); );
} }

View File

@@ -8,10 +8,6 @@ export function AppShelfItemWrapper(props: any) {
style={{ style={{
boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)', boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)',
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[1], backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[1],
//TODO: #3 Fix this temporary fix and make the width and height dynamic / responsive
width: 200,
height: 180,
}} }}
radius="md" radius="md"
> >

View File

@@ -122,7 +122,7 @@ export function SettingsMenuButton(props: any) {
</Modal> </Modal>
<ActionIcon <ActionIcon
variant="default" variant="default"
radius="xl" radius="md"
size="xl" size="xl"
color="blue" color="blue"
style={props.style} style={props.style}

View File

@@ -72,7 +72,7 @@ export function Footer({ links }: FooterCenteredProps) {
}} }}
> >
Made with by @ Made with by @
<Anchor href="https://github.com/ajnart" style={{ color: 'inherit', fontStyle: 'inherit' }}> <Anchor href="https://github.com/ajnart" style={{ color: 'inherit', fontStyle: 'inherit', fontSize: 'inherit' }}>
ajnart ajnart
</Anchor> </Anchor>
</Text> </Text>

View File

@@ -1,18 +1,11 @@
import React, { useState } from 'react'; import React from 'react';
import { import { createStyles, Header as Head, Group, Drawer, Center } from '@mantine/core';
createStyles,
Header as Head,
Container,
Group,
Burger,
Drawer,
Center,
} from '@mantine/core';
import { useBooleanToggle } from '@mantine/hooks'; import { useBooleanToggle } from '@mantine/hooks';
import { NextLink } from '@mantine/next'; import { NextLink } from '@mantine/next';
import { Logo } from './Logo'; import { Logo } from './Logo';
import { SettingsMenuButton } from '../Settings/SettingsMenu';
import CalendarComponent from '../modules/calendar/CalendarModule'; import CalendarComponent from '../modules/calendar/CalendarModule';
import { SettingsMenuButton } from '../Settings/SettingsMenu';
import { AddItemShelfButton } from '../AppShelf/AddAppShelfItem';
const HEADER_HEIGHT = 60; const HEADER_HEIGHT = 60;
@@ -40,8 +33,6 @@ const useStyles = createStyles((theme) => ({
header: { header: {
display: 'flex', display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '100%', height: '100%',
}, },
@@ -94,62 +85,33 @@ interface HeaderResponsiveProps {
export function Header({ links }: HeaderResponsiveProps) { export function Header({ links }: HeaderResponsiveProps) {
const [opened, toggleOpened] = useBooleanToggle(false); const [opened, toggleOpened] = useBooleanToggle(false);
const [active, setActive] = useState('/');
const { classes, cx } = useStyles(); const { classes, cx } = useStyles();
const items = (
<>
{links.map((link) => (
<NextLink
key={link.label}
href={link.link}
className={cx(classes.link, { [classes.linkActive]: active === link.link })}
onClick={(event) => {
setActive(link.link);
toggleOpened(false);
}}
>
{link.label}
</NextLink>
))}
</>
);
return ( return (
<Head height={HEADER_HEIGHT} mb={10} className={classes.root}> <Head height={HEADER_HEIGHT}>
<Container className={classes.header}> <Group direction="row" align="center" position="apart" className={classes.header} mx="xl">
<Group> <NextLink style={{ textDecoration: 'none' }} href="/">
<NextLink style={{ textDecoration: 'none' }} href="/"> <Logo style={{ fontSize: 22 }} />
<Logo style={{ fontSize: 22 }} /> </NextLink>
</NextLink>
</Group>
<Group spacing={5} className={classes.links}>
{items}
</Group>
<Group> <Group>
<SettingsMenuButton /> <SettingsMenuButton />
<AddItemShelfButton />
<Burger
opened={opened}
onClick={() => toggleOpened()}
className={classes.burger}
size="sm"
/>
</Group> </Group>
</Group>
<Drawer <Drawer
opened={opened} opened={opened}
overlayOpacity={0.55} overlayOpacity={0.55}
overlayBlur={3} overlayBlur={3}
onClose={() => toggleOpened()} onClose={() => toggleOpened()}
position="right" position="right"
> >
{opened ?? ( {opened ?? (
<Center> <Center>
<CalendarComponent /> <CalendarComponent />
</Center> </Center>
)} )}
</Drawer> </Drawer>
</Container>
</Head> </Head>
); );
} }

View File

@@ -7,7 +7,7 @@ import Navbar from './Navbar';
const useStyles = createStyles((theme) => ({ const useStyles = createStyles((theme) => ({
main: { main: {
[theme.fn.largerThan('md')]: { [theme.fn.largerThan('md')]: {
maxWidth: 1200, maxWidth: 1500,
}, },
}, },
})); }));

View File

@@ -1,15 +1,29 @@
import { Text } from '@mantine/core'; import { Group, Text } from '@mantine/core';
import * as React from 'react'; import * as React from 'react';
import { CURRENT_VERSION } from '../../../data/constants';
export function Logo({ style }: any) { export function Logo({ style }: any) {
return ( return (
<Text <Group>
sx={style} <Text
weight="bold" sx={style}
variant="gradient" weight="bold"
gradient={{ from: 'red', to: 'orange', deg: 145 }} variant="gradient"
> gradient={{ from: 'red', to: 'orange', deg: 145 }}
Homarr >
</Text> Homarr
</Text>
<Text
style={{
color: 'gray',
fontStyle: 'inherit',
fontSize: 'inherit',
alignSelf: 'end',
alignContent: 'start',
}}
>
{CURRENT_VERSION}
</Text>
</Group>
); );
} }

View File

@@ -6,7 +6,7 @@ export default function Navbar() {
return ( return (
<MantineNavbar <MantineNavbar
height="100%" height="100%"
hiddenBreakpoint="md" hiddenBreakpoint="lg"
hidden hidden
width={{ width={{
base: 'auto', base: 'auto',