Add propagation for service name to service icon

This commit is contained in:
Manuel Ruwe
2022-12-11 19:32:51 +01:00
parent ed64d138c5
commit 05e47b5664
7 changed files with 97 additions and 74 deletions

View File

@@ -1,7 +1,6 @@
import { Alert, Button, createStyles, Group, Stack, Tabs, Text, ThemeIcon } from '@mantine/core';
import { useForm } from '@mantine/form';
import { ContextModalProps } from '@mantine/modals';
import { hideNotification, showNotification } from '@mantine/notifications';
import {
IconAccessPoint,
IconAdjustments,
@@ -11,7 +10,6 @@ import {
IconPlug,
} from '@tabler/icons';
import { useTranslation } from 'next-i18next';
import Image from 'next/image';
import { useState } from 'react';
import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store';
@@ -31,11 +29,13 @@ export const EditServiceModal = ({
context,
id,
innerProps,
}: ContextModalProps<{ service: ServiceType }>) => {
}: ContextModalProps<{ service: ServiceType; allowServiceNamePropagation: boolean }>) => {
const { t } = useTranslation();
const { classes } = useStyles();
const { name: configName, config } = useConfigContext();
const updateConfig = useConfigStore((store) => store.updateConfig);
const [allowServiceNamePropagation, setAllowServiceNamePropagation] = useState<boolean>(
innerProps.allowServiceNamePropagation
);
const form = useForm<ServiceType>({
initialValues: innerProps.service,
@@ -94,29 +94,7 @@ export const EditServiceModal = ({
const [activeTab, setActiveTab] = useState<EditServiceModalTab>('general');
const tryCloseModal = () => {
if (form.isDirty()) {
showNotification({
id: 'unsaved-edit-service-modal-changes',
title: 'You have unsaved changes',
message: (
<Stack>
<Text color="dimmed">If you close, your changes will be discarded and not saved.</Text>
<Button
onClick={() => {
context.closeModal(id);
hideNotification('unsaved-edit-service-modal-changes');
}}
variant="light"
>
Close anyway
</Button>
</Stack>
),
});
return;
}
const closeModal = () => {
context.closeModal(id);
};
@@ -196,12 +174,16 @@ export const EditServiceModal = ({
<GeneralTab form={form} openTab={(targetTab) => setActiveTab(targetTab)} />
<BehaviourTab form={form} />
<NetworkTab form={form} />
<AppearanceTab form={form} />
<AppearanceTab
form={form}
disallowServiceNameProgagation={() => setAllowServiceNamePropagation(false)}
allowServiceNamePropagation={allowServiceNamePropagation}
/>
<IntegrationTab form={form} />
</Tabs>
<Group position="right" mt={100}>
<Button onClick={tryCloseModal} px={50} variant="light" color="gray">
<Button onClick={closeModal} px={50} variant="light" color="gray">
Cancel
</Button>
<Button disabled={!form.isValid()} px={50} type="submit">

View File

@@ -7,9 +7,15 @@ import { IconSelector } from './IconSelector/IconSelector';
interface AppearanceTabProps {
form: UseFormReturnType<ServiceType, (values: ServiceType) => ServiceType>;
disallowServiceNameProgagation: () => void;
allowServiceNamePropagation: boolean;
}
export const AppearanceTab = ({ form }: AppearanceTabProps) => {
export const AppearanceTab = ({
form,
disallowServiceNameProgagation,
allowServiceNamePropagation,
}: AppearanceTabProps) => {
const { t } = useTranslation('');
const { classes } = useStyles();
@@ -28,13 +34,16 @@ export const AppearanceTab = ({ form }: AppearanceTabProps) => {
{...form.getInputProps('appearance.iconUrl')}
/>
<IconSelector
onChange={(item) =>
onChange={(item) => {
form.setValues({
appearance: {
iconUrl: item.url,
},
})
}
});
disallowServiceNameProgagation();
}}
allowServiceNamePropagation={allowServiceNamePropagation}
form={form}
/>
</Flex>
</Tabs.Panel>

View File

@@ -13,22 +13,32 @@ import {
TextInput,
Title,
} from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { useDebouncedValue } from '@mantine/hooks';
import { IconSearch, IconX } from '@tabler/icons';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { ICON_PICKER_SLICE_LIMIT } from '../../../../../../../../data/constants';
import { useRepositoryIconsQuery } from '../../../../../../../tools/hooks/useRepositoryIconsQuery';
import { IconSelectorItem } from '../../../../../../../types/iconSelector/iconSelectorItem';
import { WalkxcodeRepositoryIcon } from '../../../../../../../types/iconSelector/repositories/walkxcodeIconRepository';
import { ServiceType } from '../../../../../../../types/service';
interface IconSelectorProps {
form: UseFormReturnType<ServiceType, (values: ServiceType) => ServiceType>;
onChange: (icon: IconSelectorItem) => void;
allowServiceNamePropagation: boolean;
}
export const IconSelector = ({ onChange }: IconSelectorProps) => {
export const IconSelector = ({
onChange,
allowServiceNamePropagation,
form,
}: IconSelectorProps) => {
const { data, isLoading } = useRepositoryIconsQuery<WalkxcodeRepositoryIcon>({
url: 'https://api.github.com/repos/walkxcode/Dashboard-Icons/contents/png',
converter: (item) => ({
url: `https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${item.name}`,
fileName: item.name,
}),
});
@@ -39,8 +49,7 @@ export const IconSelector = ({ onChange }: IconSelectorProps) => {
return <Loader />;
}
const replaceCharacters = (value: string) =>
value.toLowerCase().replaceAll(' ', '').replaceAll('-', '');
const replaceCharacters = (value: string) => value.toLowerCase().replaceAll('', '-');
const filteredItems = searchTerm
? data.filter((x) => replaceCharacters(x.url).includes(replaceCharacters(searchTerm)))
@@ -49,6 +58,24 @@ export const IconSelector = ({ onChange }: IconSelectorProps) => {
const isTruncated =
slicedFilteredItems.length > 0 && slicedFilteredItems.length !== filteredItems.length;
const [debouncedValue] = useDebouncedValue(form.values.name, 500);
useEffect(() => {
if (allowServiceNamePropagation !== true) {
return;
}
const matchingDebouncedIcon = data.find(
(x) => replaceCharacters(x.fileName.split('.')[0]) === replaceCharacters(debouncedValue)
);
if (!matchingDebouncedIcon) {
return;
}
form.setFieldValue('appearance.iconUrl', matchingDebouncedIcon.url);
}, [debouncedValue]);
return (
<Popover width={310}>
<Popover.Target>

View File

@@ -1,4 +1,4 @@
import { Group, Tabs, Text, TextInput } from '@mantine/core';
import { Tabs, Text, TextInput } from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { IconCursorText, IconLink } from '@tabler/icons';
import { useTranslation } from 'next-i18next';

View File

@@ -27,44 +27,47 @@ export const AvailableElementTypes = ({
name="Service"
icon={<IconBox size={40} strokeWidth={1.3} />}
onClick={() => {
openContextModalGeneric<{ service: ServiceType }>({
modal: 'editService',
innerProps: {
service: {
id: uuidv4(),
name: 'Your service',
url: 'https://homarr.dev',
appearance: {
iconUrl: '/imgs/logo/logo.png',
},
network: {
enabledStatusChecker: false,
okStatus: [],
},
behaviour: {
isOpeningNewTab: true,
onClickUrl: '',
},
area: {
type: 'sidebar',
properties: {
location: 'right',
},
},
shape: {
location: {
x: 0,
y: 0,
},
size: {
height: 1,
width: 1,
openContextModalGeneric<{ service: ServiceType; allowServiceNamePropagation: boolean }>(
{
modal: 'editService',
innerProps: {
service: {
id: uuidv4(),
name: 'Your service',
url: 'https://homarr.dev',
appearance: {
iconUrl: '/imgs/logo/logo.png',
},
network: {
enabledStatusChecker: false,
okStatus: [],
},
behaviour: {
isOpeningNewTab: true,
onClickUrl: '',
},
area: {
type: 'sidebar',
properties: {
location: 'right',
},
},
shape: {
location: {
x: 0,
y: 0,
},
size: {
height: 1,
width: 1,
},
},
},
allowServiceNamePropagation: true,
},
},
size: 'xl',
});
size: 'xl',
}
);
}}
/>
<ElementItem

View File

@@ -8,11 +8,12 @@ interface TileMenuProps {
export const ServiceMenu = ({ service }: TileMenuProps) => {
const handleClickEdit = () => {
openContextModalGeneric<{ service: ServiceType }>({
openContextModalGeneric<{ service: ServiceType; allowServiceNamePropagation: boolean }>({
modal: 'editService',
size: 'xl',
innerProps: {
service,
allowServiceNamePropagation: false,
},
});
};