Add generic menu for tiles

This commit is contained in:
Manuel Ruwe
2022-12-11 00:00:11 +01:00
parent bfdd6b5181
commit d78f0384fa
10 changed files with 155 additions and 166 deletions

View File

@@ -2,7 +2,14 @@
"actions": {
"save": "Save",
"cancel": "Cancel",
"ok": "Okay"
"ok": "Okay",
"edit": "Edit",
"changePosition": "Change position",
"remove": "Remove"
},
"sections": {
"settings": "Settings",
"dangerZone": "Danger zone"
},
"tip": "Tip: ",
"time": {

View File

@@ -15,7 +15,6 @@ import { useState } from 'react';
import PingComponent from '../../modules/ping/PingModule';
import { useConfig } from '../../tools/state';
import { serviceItem } from '../../tools/types';
import { TileMenu } from '../Dashboard/Menu/TileMenu';
const useStyles = createStyles((theme) => ({
item: {
@@ -104,7 +103,7 @@ export function AppShelfItem(props: any) {
opacity: hovering ? 1 : 0,
}}
>
{ /* <TileMenu service={service} /> TODO: Remove this component */ }
{/* <TileMenu service={service} /> TODO: Remove this component */}
</motion.div>
</Card.Section>
<Card.Section>

View File

@@ -1,76 +0,0 @@
import { ActionIcon, Menu, Text } from '@mantine/core';
import { openContextModal } from '@mantine/modals';
import { showNotification } from '@mantine/notifications';
import { IconCheck, IconEdit, IconMenu, IconTrash } from '@tabler/icons';
import { useTranslation } from 'next-i18next';
import { useConfigContext } from '../../../config/provider';
import { useConfigStore } from '../../../config/store';
import { useColorTheme } from '../../../tools/color';
import { ServiceType } from '../../../types/service';
interface TileMenuProps {
service: ServiceType;
}
export const TileMenu = ({ service }: TileMenuProps) => {
const { secondaryColor } = useColorTheme();
const { t } = useTranslation('layout/app-shelf-menu');
const updateConfig = useConfigStore((x) => x.updateConfig);
const { name: configName } = useConfigContext();
return (
<Menu withinPortal width={150} shadow="xl" withArrow radius="md" position="right">
<Menu.Target>
<ActionIcon>
<IconMenu />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>{t('menu.labels.settings')}</Menu.Label>
<Menu.Item
color={secondaryColor}
icon={<IconEdit />}
onClick={() =>
openContextModal({
modal: 'changeTilePosition',
innerProps: {
type: 'service',
tile: service,
},
})
}
>
{t('menu.actions.edit')}
</Menu.Item>
<Menu.Label>{t('menu.labels.dangerZone')}</Menu.Label>
<Menu.Item
color="red"
onClick={(e: any) => {
if (!configName) {
return;
}
updateConfig(configName, (previousConfig) => ({
...previousConfig,
services: previousConfig.services.filter((x) => x.id !== service.id),
})).then(() => {
showNotification({
autoClose: 5000,
title: (
<Text>
Service <b>{service.name}</b> removed successfully!
</Text>
),
color: 'green',
icon: <IconCheck />,
message: undefined,
});
});
}}
icon={<IconTrash />}
>
{t('menu.actions.delete')}
</Menu.Item>
</Menu.Dropdown>
</Menu>
);
};

View File

@@ -1,4 +1,4 @@
import { Card, Center, Stack, Text, Title } from '@mantine/core';
import { Center, Stack, Text, Title } from '@mantine/core';
import dayjs from 'dayjs';
import { useEffect, useRef, useState } from 'react';
import { useSetSafeInterval } from '../../../../tools/hooks/useSetSafeInterval';

View File

@@ -0,0 +1,57 @@
import { ActionIcon, Menu } from '@mantine/core';
import { IconDots, IconLayoutKanban, IconPencil, IconTrash } from '@tabler/icons';
import { useTranslation } from 'next-i18next';
import { useEditModeStore } from '../Views/useEditModeStore';
interface GenericTileMenuProps {
handleClickEdit: () => void;
handleClickChangePosition: () => void;
handleClickDelete: () => void;
displayEdit: boolean;
}
export const GenericTileMenu = ({
handleClickEdit,
handleClickChangePosition,
handleClickDelete,
displayEdit,
}: GenericTileMenuProps) => {
const { t } = useTranslation('common');
const isEditMode = useEditModeStore((x) => x.enabled);
if (!isEditMode) {
return null;
}
return (
<Menu withinPortal>
<Menu.Target>
<ActionIcon pos="absolute" top={4} right={4}>
<IconDots />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown w={250}>
<Menu.Label>{t('sections.settings')}</Menu.Label>
{displayEdit && (
<Menu.Item icon={<IconPencil size={16} stroke={1.5} />} onClick={handleClickEdit}>
{t('actions.edit')}
</Menu.Item>
)}
<Menu.Item
icon={<IconLayoutKanban size={16} stroke={1.5} />}
onClick={handleClickChangePosition}
>
{t('actions.changePosition')}
</Menu.Item>
<Menu.Label>{t('sections.dangerZone')}</Menu.Label>
<Menu.Item
color="red"
icon={<IconTrash size={16} stroke={1.5} color="red" />}
onClick={handleClickDelete}
>
{t('actions.remove')}
</Menu.Item>
</Menu.Dropdown>
</Menu>
);
};

View File

@@ -1,4 +1,3 @@
import React, { ReactNode } from 'react';
import {
Button,
Center,
@@ -10,14 +9,10 @@ import {
SelectItem,
Stack,
Text,
TextInput,
Title,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { ContextModalProps } from '@mantine/modals';
import { useTranslation } from 'next-i18next';
import { ClockIntegrationType, IntegrationsType } from '../../../types/integration';
import { integrationModuleTranslationsMap } from './IntegrationsEditModal';
import { TileBaseType } from '../../../types/tile';
import {
IconArrowsUpDown,
IconCalendarTime,
@@ -25,11 +20,14 @@ import {
IconCloudRain,
IconFileDownload,
} from '@tabler/icons';
import { ServiceIcon } from './Service/ServiceIcon';
import { useForm } from '@mantine/form';
import { Tiles } from './tilesDefinitions';
import { useTranslation } from 'next-i18next';
import { useConfigContext } from '../../../config/provider';
import { useConfigStore } from '../../../config/store';
import { IntegrationsType } from '../../../types/integration';
import { TileBaseType } from '../../../types/tile';
import { integrationModuleTranslationsMap } from './IntegrationsEditModal';
import { ServiceIcon } from './Service/ServiceIcon';
import { Tiles } from './tilesDefinitions';
export type IntegrationChangePositionModalInnerProps = {
integration: keyof IntegrationsType;

View File

@@ -1,9 +1,9 @@
import { ActionIcon, Menu, Title } from '@mantine/core';
import { IconDots, IconLayoutKanban, IconPencil, IconTrash } from '@tabler/icons';
import { Title } from '@mantine/core';
import { useTranslation } from 'next-i18next';
import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions';
import { IntegrationsType } from '../../../../types/integration';
import { TileBaseType } from '../../../../types/tile';
import { GenericTileMenu } from '../GenericTileMenu';
import { IntegrationChangePositionModalInnerProps } from '../IntegrationChangePositionModal';
import { IntegrationRemoveModalInnerProps } from '../IntegrationRemoveModal';
import {
@@ -65,34 +65,11 @@ export const IntegrationsMenu = <TIntegrationKey extends keyof IntegrationsType>
};
return (
<Menu withinPortal>
<Menu.Target>
<ActionIcon pos="absolute" top={4} right={4}>
<IconDots />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown w={250}>
<Menu.Label>Settings</Menu.Label>
{options && (
<Menu.Item icon={<IconPencil size={16} stroke={1.5} />} onClick={handleEditClick}>
Edit
</Menu.Item>
)}
<Menu.Item
icon={<IconLayoutKanban size={16} stroke={1.5} />}
onClick={handleChangeSizeClick}
>
Change position
</Menu.Item>
<Menu.Label>Danger zone</Menu.Label>
<Menu.Item
color="red"
icon={<IconTrash size={16} stroke={1.5} color="red" />}
onClick={handleDeleteClick}
>
Remove
</Menu.Item>
</Menu.Dropdown>
</Menu>
<GenericTileMenu
handleClickEdit={handleEditClick}
handleClickChangePosition={handleChangeSizeClick}
handleClickDelete={handleDeleteClick}
displayEdit={options !== undefined}
/>
);
};

View File

@@ -0,0 +1,39 @@
import { openContextModalGeneric } from '../../../../tools/mantineModalManagerExtensions';
import { ServiceType } from '../../../../types/service';
import { GenericTileMenu } from '../GenericTileMenu';
interface TileMenuProps {
service: ServiceType;
}
export const ServiceMenu = ({ service }: TileMenuProps) => {
const handleClickEdit = () => {
openContextModalGeneric<{ service: ServiceType }>({
modal: 'editService',
size: 'xl',
innerProps: {
service,
},
});
};
const handleClickChangePosition = () => {
openContextModalGeneric({
modal: 'changeTilePosition',
innerProps: {
tile: service,
},
});
};
const handleClickDelete = () => {};
return (
<GenericTileMenu
handleClickEdit={handleClickEdit}
handleClickChangePosition={handleClickChangePosition}
handleClickDelete={handleClickDelete}
displayEdit
/>
);
};

View File

@@ -1,11 +1,11 @@
import { ActionIcon, Card, Center, Text, UnstyledButton } from '@mantine/core';
import { Card, Center, Text, UnstyledButton } from '@mantine/core';
import { NextLink } from '@mantine/next';
import { createStyles } from '@mantine/styles';
import { IconDots } from '@tabler/icons';
import { ServiceType } from '../../../../types/service';
import { useCardStyles } from '../../../layout/useCardStyles';
import { useEditModeStore } from '../../Views/useEditModeStore';
import { BaseTileProps } from '../type';
import { ServiceMenu } from './ServiceMenu';
interface ServiceTileProps extends BaseTileProps {
service: ServiceType;
@@ -26,6 +26,7 @@ export const ServiceTile = ({ className, service }: ServiceTileProps) => {
{service.name}
</Text>
<Center style={{ height: '75%', flex: 1 }}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img className={classes.image} src={service.appearance.iconUrl} alt="" />
</Center>
</>
@@ -34,6 +35,11 @@ export const ServiceTile = ({ className, service }: ServiceTileProps) => {
return (
<Card className={cx(className, cardClass)} withBorder radius="lg" shadow="md">
{/* TODO: add service menu */}
<div style={{ position: 'absolute', top: 10, right: 10 }}>
<ServiceMenu service={service} />
</div>
{!service.url || isEditMode ? (
<UnstyledButton
className={classes.button}
@@ -57,32 +63,30 @@ export const ServiceTile = ({ className, service }: ServiceTileProps) => {
);
};
const useStyles = createStyles((theme, _params, getRef) => {
return {
image: {
ref: getRef('image'),
maxHeight: '80%',
maxWidth: '80%',
transition: 'transform 100ms ease-in-out',
const useStyles = createStyles((theme, _params, getRef) => ({
image: {
ref: getRef('image'),
maxHeight: '80%',
maxWidth: '80%',
transition: 'transform 100ms ease-in-out',
},
serviceName: {
ref: getRef('serviceName'),
},
button: {
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 4,
},
link: {
[`&:hover .${getRef('image')}`]: {
// TODO: add styles for image when hovering card
},
serviceName: {
ref: getRef('serviceName'),
[`&:hover .${getRef('serviceName')}`]: {
// TODO: add styles for service name when hovering card
},
button: {
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 4,
},
link: {
[`&:hover .${getRef('image')}`]: {
// TODO: add styles for image when hovering card
},
[`&:hover .${getRef('serviceName')}`]: {
// TODO: add styles for service name when hovering card
},
},
};
});
},
}));

View File

@@ -1,5 +1,4 @@
import { Box, createStyles, Group, Header as MantineHeader, Text } from '@mantine/core';
import { openContextModal } from '@mantine/modals';
import { Box, createStyles, Group, Header as MantineHeader } from '@mantine/core';
import { useConfigContext } from '../../../config/provider';
import { Logo } from '../Logo';
import { useCardStyles } from '../useCardStyles';
@@ -23,21 +22,6 @@ export function Header(props: any) {
<Logo />
</Box>
<Group position="right" style={{ maxWidth: 'none' }} noWrap>
<Text
onClick={() => {
openContextModal({
modal: 'changeTilePosition',
title: 'Change tile position',
innerProps: {
tile: config?.services[0],
},
});
}}
variant="link"
>
Test
</Text>
<Search />
<AddElementAction />
<ToggleEditModeAction />