🎨 Merge change position modals

This commit is contained in:
Manuel Ruwe
2022-12-11 13:12:39 +01:00
parent 8586f0d8ec
commit 40c9b5d203
7 changed files with 224 additions and 212 deletions

View File

@@ -0,0 +1,89 @@
import { SelectItem } from '@mantine/core';
import { closeModal, ContextModalProps } from '@mantine/modals';
import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store';
import { IntegrationsType } from '../../../../types/integration';
import { TileBaseType } from '../../../../types/tile';
import { Tiles } from '../../Tiles/tilesDefinitions';
import { ChangePositionModal } from './ChangePositionModal';
export type IntegrationChangePositionModalInnerProps = {
integration: keyof IntegrationsType;
module: TileBaseType;
};
export const ChangeIntegrationPositionModal = ({
context,
id,
innerProps,
}: ContextModalProps<IntegrationChangePositionModalInnerProps>) => {
const { name: configName } = useConfigContext();
const updateConfig = useConfigStore((x) => x.updateConfig);
const handleSubmit = (x: number, y: number, width: number, height: number) => {
if (!configName) {
return;
}
updateConfig(configName, (prev) => ({
...prev,
integrations: {
...prev.integrations,
[innerProps.integration]: {
...prev.integrations[innerProps.integration],
shape: {
location: {
x,
y,
},
size: {
height,
width,
},
},
},
},
}));
context.closeModal(id);
};
const handleCancel = () => {
closeModal(id);
};
const widthData = useWidthData(innerProps.integration);
const heightData = useHeightData(innerProps.integration);
return (
<ChangePositionModal
onSubmit={handleSubmit}
onCancel={handleCancel}
heightData={heightData}
widthData={widthData}
initialX={innerProps.module.shape.location.x}
initialY={innerProps.module.shape.location.y}
initialWidth={innerProps.module.shape.size.width}
initialHeight={innerProps.module.shape.size.height}
/>
);
};
const useWidthData = (integration: keyof IntegrationsType): SelectItem[] => {
const tileDefinitions = Tiles[integration];
const offset = tileDefinitions.minWidth ?? 2;
const length = (tileDefinitions.maxWidth ?? 12) - offset;
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
value: n.toString(),
label: `${64 * n}px`,
}));
};
const useHeightData = (integration: keyof IntegrationsType): SelectItem[] => {
const tileDefinitions = Tiles[integration];
const offset = tileDefinitions.minHeight ?? 2;
const length = (tileDefinitions.maxHeight ?? 12) - offset;
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
value: n.toString(),
label: `${64 * n}px`,
}));
};

View File

@@ -1,44 +1,55 @@
import { Button, Flex, Grid, NumberInput } from '@mantine/core';
import { Button, Flex, Grid, NumberInput, Select, SelectItem } from '@mantine/core';
import { useForm } from '@mantine/form';
import { closeModal, ContextModalProps } from '@mantine/modals';
import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store';
import { ServiceType } from '../../../../types/service';
import { TileBaseType } from '../../../../types/tile';
interface ChangePositionModalProps {
initialX: number;
initialY: number;
initialWidth: number;
initialHeight: number;
widthData: SelectItem[];
heightData: SelectItem[];
onSubmit: (x: number, y: number, width: number, height: number) => void;
onCancel: () => void;
}
export const ChangePositionModal = ({
context,
id,
innerProps,
}: ContextModalProps<{ type: 'service' | 'type'; tile: TileBaseType }>) => {
const updateConfig = useConfigStore((x) => x.updateConfig);
initialX,
initialY,
initialWidth,
initialHeight,
widthData,
heightData,
onCancel,
onSubmit,
}: ChangePositionModalProps) => {
const { name: configName } = useConfigContext();
const form = useForm({
const form = useForm<FormType>({
initialValues: {
tile: innerProps.tile,
x: initialX,
y: initialY,
width: initialWidth,
height: initialHeight,
},
validateInputOnChange: true,
validateInputOnBlur: true,
});
const onSubmit = () => {
const handleSubmit = () => {
if (!configName) {
return;
}
const tileAsService = form.values.tile as ServiceType;
updateConfig(configName, (previous) => ({
...previous,
services: [...previous.services.filter((x) => x.id === tileAsService.id), tileAsService],
}));
closeModal(id);
onSubmit(form.values.x, form.values.y, form.values.width, form.values.height);
};
console.log(`Initial: (${form.values.width} / ${form.values.height})`);
console.log(widthData);
console.log(heightData);
return (
<form onSubmit={form.onSubmit(onSubmit)}>
<form onSubmit={form.onSubmit(handleSubmit)}>
<Grid>
<Grid.Col xs={12} md={6}>
<NumberInput
@@ -46,7 +57,7 @@ export const ChangePositionModal = ({
min={0}
label="X Position"
description="0 or higher"
{...form.getInputProps('tile.shape.location.x')}
{...form.getInputProps('x')}
/>
</Grid.Col>
@@ -56,39 +67,48 @@ export const ChangePositionModal = ({
min={0}
label="Y Position"
description="0 or higher"
{...form.getInputProps('tile.shape.location.y')}
{...form.getInputProps('y')}
/>
</Grid.Col>
</Grid>
<Grid>
<Grid.Col xs={12} md={6}>
<NumberInput
<Select
data={widthData}
max={24}
min={1}
label="Width"
description="Between 1 and 24"
{...form.getInputProps('tile.shape.size.width')}
description={`Between ${widthData.at(0)?.label} and ${widthData.at(-1)?.label}`}
{...form.getInputProps('width')}
/>
</Grid.Col>
<Grid.Col xs={12} md={6}>
<NumberInput
<Select
data={heightData}
max={24}
min={1}
label="Height"
description="Between 1 and 24"
{...form.getInputProps('tile.shape.size.height')}
description={`Between ${heightData.at(0)?.label} and ${heightData.at(-1)?.label}`}
{...form.getInputProps('height')}
/>
</Grid.Col>
</Grid>
<Flex justify="end" gap="sm" mt="md">
<Button onClick={() => closeModal(id)} variant="light" color="gray">
<Button onClick={() => onCancel()} variant="light" color="gray">
Cancel
</Button>
<Button type="submit">Change Position</Button>
<Button type="submit">Save</Button>
</Flex>
</form>
);
};
type FormType = {
x: number;
y: number;
width: number;
height: number;
};

View File

@@ -0,0 +1,72 @@
import { SelectItem } from '@mantine/core';
import { closeModal, ContextModalProps } from '@mantine/modals';
import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store';
import { ServiceType } from '../../../../types/service';
import { ChangePositionModal } from './ChangePositionModal';
type ChangeServicePositionModalInnerProps = {
service: ServiceType;
};
export const ChangeServicePositionModal = ({
id,
context,
innerProps,
}: ContextModalProps<ChangeServicePositionModalInnerProps>) => {
const { name: configName } = useConfigContext();
const updateConfig = useConfigStore((x) => x.updateConfig);
const handleSubmit = (x: number, y: number, width: number, height: number) => {
if (!configName) {
return;
}
updateConfig(configName, (previousConfig) => ({
...previousConfig,
services: [
...previousConfig.services.filter((x) => x.id !== innerProps.service.id),
{ ...innerProps.service, shape: { location: { x, y }, size: { width, height } } },
],
}));
context.closeModal(id);
};
const handleCancel = () => {
closeModal(id);
};
const widthData = useWidthData();
const heightData = useHeightData();
return (
<ChangePositionModal
onSubmit={handleSubmit}
onCancel={handleCancel}
widthData={widthData}
heightData={heightData}
initialX={innerProps.service.shape.location.x}
initialY={innerProps.service.shape.location.y}
initialWidth={innerProps.service.shape.size.width}
initialHeight={innerProps.service.shape.size.height}
/>
);
};
const useHeightData = (): SelectItem[] =>
Array.from(Array(11).keys()).map((n) => {
const index = n + 1;
return {
value: index.toString(),
label: `${64 * index}px`,
};
});
const useWidthData = (): SelectItem[] =>
Array.from(Array(11).keys()).map((n) => {
const index = n + 1;
return {
value: index.toString(),
label: `${64 * index}px`,
};
});

View File

@@ -1,169 +0,0 @@
import {
Button,
Center,
createStyles,
Grid,
Group,
NumberInput,
Select,
SelectItem,
Stack,
Text,
Title,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { ContextModalProps } from '@mantine/modals';
import {
IconArrowsUpDown,
IconCalendarTime,
IconClock,
IconCloudRain,
IconFileDownload,
} from '@tabler/icons';
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;
module: TileBaseType;
};
export const IntegrationChangePositionModal = ({
context,
id,
innerProps,
}: ContextModalProps<IntegrationChangePositionModalInnerProps>) => {
const translationKey = integrationModuleTranslationsMap.get(innerProps.integration);
const { t } = useTranslation([translationKey ?? '', 'common']);
const { classes } = useStyles();
const { name: configName } = useConfigContext();
const updateConfig = useConfigStore((x) => x.updateConfig);
const form = useForm<FormType>({
initialValues: {
x: innerProps.module.shape.location.x,
y: innerProps.module.shape.location.y,
width: innerProps.module.shape.size.width.toString(),
height: innerProps.module.shape.size.height.toString(),
},
});
if (!configName) return null;
null;
const handleSubmit = (values: FormType) => {
updateConfig(configName, (prev) => {
return {
...prev,
integrations: {
...prev.integrations,
[innerProps.integration]: {
...prev.integrations[innerProps.integration],
shape: {
location: {
x: values.x,
y: values.y,
},
size: {
height: parseInt(values.height),
width: parseInt(values.width),
},
},
},
},
};
});
context.closeModal(id);
};
const widthData = useWidthData(innerProps.integration);
const heightData = useHeightData(innerProps.integration);
return (
<Stack>
<Stack spacing={0}>
<Center>
<div className={classes.icon}>{integrationIcons[innerProps.integration]}</div>
</Center>
<Text align="center" size="sm">
Change position of
</Text>
<Title align="center" order={4}>
{t('descriptor.name')}
</Title>
</Stack>
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
<Grid>
<Grid.Col span={12} xs={6}>
<NumberInput label="X Position" required {...form.getInputProps('x')} />
</Grid.Col>
<Grid.Col span={12} xs={6}>
<NumberInput label="Y Position" required {...form.getInputProps('y')} />
</Grid.Col>
<Grid.Col span={12} xs={6}>
<Select data={widthData} label="Width" required {...form.getInputProps('width')} />
</Grid.Col>
<Grid.Col span={12} xs={6}>
<Select data={heightData} label="Height" required {...form.getInputProps('height')} />
</Grid.Col>
</Grid>
<Group position="right">
<Button onClick={() => context.closeModal(id)} variant="light">
{t('common:actions.cancel')}
</Button>
<Button type="submit">{t('common:actions.save')}</Button>
</Group>
</Stack>
</form>
</Stack>
);
};
type FormType = {
x: number;
y: number;
width: string;
height: string;
};
// TODO: define width of gridstack somewhere (64)
const useWidthData = (integration: keyof IntegrationsType): SelectItem[] => {
const tileDefinitions = Tiles[integration];
const offset = tileDefinitions.minWidth ?? 2;
const length = (tileDefinitions.maxWidth ?? 12) - offset;
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
value: n.toString(),
label: `${64 * n}px`,
}));
};
const useHeightData = (integration: keyof IntegrationsType): SelectItem[] => {
const tileDefinitions = Tiles[integration];
const offset = tileDefinitions.minHeight ?? 2;
const length = (tileDefinitions.maxHeight ?? 12) - offset;
return Array.from({ length }, (_, i) => i + offset).map((n) => ({
value: n.toString(),
label: `${64 * n}px`,
}));
};
const integrationIcons = {
useNet: <IconFileDownload size="100%" />,
bitTorrent: <IconFileDownload size="100%" />,
calendar: <IconCalendarTime size="100%" />,
clock: <IconClock size="100%" />,
weather: <IconCloudRain size="100%" />,
dashDot: <ServiceIcon size="100%" service="dashdot" />,
torrentNetworkTraffic: <IconArrowsUpDown size="100%" />,
};
const useStyles = createStyles(() => ({
icon: {
height: 120,
width: 120,
},
}));

View File

@@ -42,7 +42,7 @@ export const IntegrationsMenu = <TIntegrationKey extends keyof IntegrationsType>
const handleChangeSizeClick = () => {
openContextModalGeneric<IntegrationChangePositionModalInnerProps>({
modal: 'integrationChangePosition',
modal: 'changeIntegrationPositionModal',
size: 'xl',
title: null,
innerProps: {

View File

@@ -19,9 +19,9 @@ export const ServiceMenu = ({ service }: TileMenuProps) => {
const handleClickChangePosition = () => {
openContextModalGeneric({
modal: 'changeTilePosition',
modal: 'changeServicePositionModal',
innerProps: {
tile: service,
service,
},
});
};

View File

@@ -9,18 +9,18 @@ import { appWithTranslation } from 'next-i18next';
import { AppProps } from 'next/app';
import Head from 'next/head';
import { useState } from 'react';
import { IntegrationsEditModal } from '../components/Dashboard/Tiles/IntegrationsEditModal';
import { IntegrationRemoveModal } from '../components/Dashboard/Tiles/IntegrationRemoveModal';
import { ChangePositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangePositionModal';
import { ChangeIntegrationPositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeIntegrationPositionModal';
import { ChangeServicePositionModal } from '../components/Dashboard/Modals/ChangePosition/ChangeServicePositionModal';
import { EditServiceModal } from '../components/Dashboard/Modals/EditService/EditServiceModal';
import { SelectElementModal } from '../components/Dashboard/Modals/SelectElement/SelectElementModal';
import { IntegrationRemoveModal } from '../components/Dashboard/Tiles/IntegrationRemoveModal';
import { IntegrationsEditModal } from '../components/Dashboard/Tiles/IntegrationsEditModal';
import { CategoryEditModal } from '../components/Dashboard/Wrappers/Category/CategoryEditModal';
import { ConfigProvider } from '../config/provider';
import '../styles/global.scss';
import { ColorTheme } from '../tools/color';
import { queryClient } from '../tools/queryClient';
import { theme } from '../tools/theme';
import { IntegrationChangePositionModal } from '../components/Dashboard/Tiles/IntegrationChangePositionModal';
import '../styles/global.scss';
import { CategoryEditModal } from '../components/Dashboard/Wrappers/Category/CategoryEditModal';
function App(this: any, props: AppProps & { colorScheme: ColorScheme }) {
const { Component, pageProps } = props;
@@ -90,9 +90,9 @@ function App(this: any, props: AppProps & { colorScheme: ColorScheme }) {
selectElement: SelectElementModal,
integrationOptions: IntegrationsEditModal,
integrationRemove: IntegrationRemoveModal,
integrationChangePosition: IntegrationChangePositionModal,
categoryEditModal: CategoryEditModal,
changeTilePosition: ChangePositionModal,
changeServicePositionModal: ChangeServicePositionModal,
changeIntegrationPositionModal: ChangeIntegrationPositionModal,
}}
>
<Component {...pageProps} />