mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 15:35:55 +01:00
✨ Add form debouncer for service icon
This commit is contained in:
@@ -21,6 +21,7 @@ import { BehaviourTab } from './Tabs/BehaviourTab/BehaviourTab';
|
|||||||
import { GeneralTab } from './Tabs/GeneralTab/GeneralTab';
|
import { GeneralTab } from './Tabs/GeneralTab/GeneralTab';
|
||||||
import { IntegrationTab } from './Tabs/IntegrationTab/IntegrationTab';
|
import { IntegrationTab } from './Tabs/IntegrationTab/IntegrationTab';
|
||||||
import { NetworkTab } from './Tabs/NetworkTab/NetworkTab';
|
import { NetworkTab } from './Tabs/NetworkTab/NetworkTab';
|
||||||
|
import { DebouncedServiceIcon } from './Tabs/Shared/DebouncedServiceIcon';
|
||||||
import { EditServiceModalTab } from './Tabs/type';
|
import { EditServiceModalTab } from './Tabs/type';
|
||||||
|
|
||||||
const serviceUrlRegex =
|
const serviceUrlRegex =
|
||||||
@@ -141,19 +142,7 @@ export const EditServiceModal = ({
|
|||||||
</Alert>
|
</Alert>
|
||||||
))}
|
))}
|
||||||
<Stack spacing={0} align="center" my="lg">
|
<Stack spacing={0} align="center" my="lg">
|
||||||
{form.values.appearance.iconUrl ? (
|
<DebouncedServiceIcon form={form} width={120} height={120} />
|
||||||
// disabled because image target is too dynamic for next image cache
|
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
|
||||||
<img
|
|
||||||
className={classes.serviceImage}
|
|
||||||
src={form.values.appearance.iconUrl}
|
|
||||||
width={120}
|
|
||||||
height={120}
|
|
||||||
alt="service icon"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Image src="/favicon-squared.png" width={120} height={120} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Text align="center" weight="bold" size="lg" mt="md">
|
<Text align="center" weight="bold" size="lg" mt="md">
|
||||||
{form.values.name ?? 'New Service'}
|
{form.values.name ?? 'New Service'}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Image from 'next/image';
|
|
||||||
import { createStyles, Flex, Tabs, TextInput } from '@mantine/core';
|
import { createStyles, Flex, Tabs, TextInput } from '@mantine/core';
|
||||||
import { UseFormReturnType } from '@mantine/form';
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { ServiceType } from '../../../../../../types/service';
|
import { ServiceType } from '../../../../../../types/service';
|
||||||
|
import { DebouncedServiceIcon } from '../Shared/DebouncedServiceIcon';
|
||||||
import { IconSelector } from './IconSelector/IconSelector';
|
import { IconSelector } from './IconSelector/IconSelector';
|
||||||
|
|
||||||
interface AppearanceTabProps {
|
interface AppearanceTabProps {
|
||||||
@@ -13,31 +13,13 @@ export const AppearanceTab = ({ form }: AppearanceTabProps) => {
|
|||||||
const { t } = useTranslation('');
|
const { t } = useTranslation('');
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|
||||||
const PreviewImage = () => {
|
|
||||||
if (form.values.appearance.iconUrl !== undefined && form.values.appearance.iconUrl.length > 0) {
|
|
||||||
// disabled due to too many dynamic targets for next image cache
|
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
|
||||||
return <img className={classes.iconImage} src={form.values.appearance.iconUrl} alt="" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Image
|
|
||||||
src="/imgs/logo/logo.png"
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
objectFit="contain"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs.Panel value="appearance" pt="lg">
|
<Tabs.Panel value="appearance" pt="lg">
|
||||||
<Flex gap={5}>
|
<Flex gap={5}>
|
||||||
<TextInput
|
<TextInput
|
||||||
defaultValue={form.values.appearance.iconUrl}
|
defaultValue={form.values.appearance.iconUrl}
|
||||||
className={classes.textInput}
|
className={classes.textInput}
|
||||||
icon={<PreviewImage />}
|
icon={<DebouncedServiceIcon form={form} width={20} height={20} />}
|
||||||
label="Service Icon"
|
label="Service Icon"
|
||||||
description="Logo of your service displayed in your dashboard. Must return a body content containg an image"
|
description="Logo of your service displayed in your dashboard. Must return a body content containg an image"
|
||||||
variant="default"
|
variant="default"
|
||||||
@@ -63,9 +45,4 @@ const useStyles = createStyles(() => ({
|
|||||||
textInput: {
|
textInput: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
},
|
},
|
||||||
iconImage: {
|
|
||||||
objectFit: 'contain',
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// disabled due to too many dynamic targets for next image cache
|
||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { createStyles, Loader } from '@mantine/core';
|
||||||
|
import { UseFormReturnType } from '@mantine/form';
|
||||||
|
import { useDebouncedValue } from '@mantine/hooks';
|
||||||
|
import { ServiceType } from '../../../../../../types/service';
|
||||||
|
|
||||||
|
interface DebouncedServiceIconProps {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
form: UseFormReturnType<ServiceType, (values: ServiceType) => ServiceType>;
|
||||||
|
debouncedWaitPeriod?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DebouncedServiceIcon = ({
|
||||||
|
form,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
debouncedWaitPeriod = 1000,
|
||||||
|
}: DebouncedServiceIconProps) => {
|
||||||
|
const { classes } = useStyles();
|
||||||
|
const [debouncedIconImageUrl] = useDebouncedValue(
|
||||||
|
form.values.appearance.iconUrl,
|
||||||
|
debouncedWaitPeriod
|
||||||
|
);
|
||||||
|
|
||||||
|
if (debouncedIconImageUrl !== form.values.appearance.iconUrl) {
|
||||||
|
return <Loader width={width} height={height} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debouncedIconImageUrl.length > 0) {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
className={classes.iconImage}
|
||||||
|
src={debouncedIconImageUrl}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
className={classes.iconImage}
|
||||||
|
src="/imgs/logo/logo.png"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useStyles = createStyles(() => ({
|
||||||
|
iconImage: {
|
||||||
|
objectFit: 'contain',
|
||||||
|
},
|
||||||
|
}));
|
||||||
Reference in New Issue
Block a user