diff --git a/apps/nextjs/public/images/apps/imdb.png b/apps/nextjs/public/images/apps/imdb.png deleted file mode 100644 index 9565159a4..000000000 Binary files a/apps/nextjs/public/images/apps/imdb.png and /dev/null differ diff --git a/apps/nextjs/public/images/apps/imdb.svg b/apps/nextjs/public/images/apps/imdb.svg new file mode 100644 index 000000000..b2a908bcb --- /dev/null +++ b/apps/nextjs/public/images/apps/imdb.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/nextjs/public/images/apps/tmdb.png b/apps/nextjs/public/images/apps/tmdb.png deleted file mode 100644 index 9f983b883..000000000 Binary files a/apps/nextjs/public/images/apps/tmdb.png and /dev/null differ diff --git a/apps/nextjs/public/images/apps/tmdb.svg b/apps/nextjs/public/images/apps/tmdb.svg new file mode 100644 index 000000000..42f31f154 --- /dev/null +++ b/apps/nextjs/public/images/apps/tmdb.svg @@ -0,0 +1 @@ +Asset 2 \ No newline at end of file diff --git a/apps/nextjs/src/components/icons/picker/icon-picker.tsx b/apps/nextjs/src/components/icons/picker/icon-picker.tsx index 91983ff7c..bd77a27d5 100644 --- a/apps/nextjs/src/components/icons/picker/icon-picker.tsx +++ b/apps/nextjs/src/components/icons/picker/icon-picker.tsx @@ -1,9 +1,23 @@ import type { FocusEventHandler } from "react"; -import { useState } from "react"; -import { Combobox, Group, Image, InputBase, Skeleton, Text, useCombobox } from "@mantine/core"; +import { startTransition, useState } from "react"; +import { + Box, + Card, + Combobox, + Flex, + Image, + Indicator, + InputBase, + Paper, + Skeleton, + Stack, + Text, + UnstyledButton, + useCombobox, +} from "@mantine/core"; import { clientApi } from "@homarr/api/client"; -import { useI18n, useScopedI18n } from "@homarr/translation/client"; +import { useScopedI18n } from "@homarr/translation/client"; interface IconPickerProps { initialValue?: string; @@ -18,10 +32,9 @@ export const IconPicker = ({ initialValue, onChange, error, onFocus, onBlur }: I const [search, setSearch] = useState(initialValue ?? ""); const [previewUrl, setPreviewUrl] = useState(initialValue ?? null); - const t = useI18n(); const tCommon = useScopedI18n("common"); - const { data, isFetching } = clientApi.icon.findIcons.useQuery({ + const [data] = clientApi.icon.findIcons.useSuspenseQuery({ searchText: search, }); @@ -29,39 +42,53 @@ export const IconPicker = ({ initialValue, onChange, error, onFocus, onBlur }: I onDropdownClose: () => combobox.resetSelectedOption(), }); - const notNullableData = data?.icons ?? []; - - const totalOptions = notNullableData.reduce((acc, group) => acc + group.icons.length, 0); - - const groups = notNullableData.map((group) => { + const totalOptions = data.icons.reduce((acc, group) => acc + group.icons.length, 0); + const groups = data.icons.map((group) => { const options = group.icons.map((item) => ( - - - - {item.name} - - + { + const value = item.url; + startTransition(() => { + setValue(value); + setPreviewUrl(value); + setSearch(value); + onChange(value); + combobox.closeDropdown(); + }); + }} + key={item.id} + > + + + + + + + + )); return ( - - {options} - + + + {group.slug} + + + {options} + + ); }); return ( - { - setValue(value); - setPreviewUrl(value); - setSearch(value); - onChange(value); - combobox.closeDropdown(); - }} - store={combobox} - withinPortal - > + } @@ -91,18 +118,14 @@ export const IconPicker = ({ initialValue, onChange, error, onFocus, onBlur }: I withAsterisk error={error} label={tCommon("iconPicker.label")} + placeholder={tCommon("iconPicker.header", { countIcons: data.countIcons })} /> - - {tCommon("iconPicker.header", { countIcons: data?.countIcons })} - {totalOptions > 0 ? ( - groups - ) : !isFetching ? ( - {t("search.nothingFound")} + {groups} ) : ( Array(15) .fill(0) diff --git a/packages/api/src/router/icons.ts b/packages/api/src/router/icons.ts index e99918da3..7b7c2f52b 100644 --- a/packages/api/src/router/icons.ts +++ b/packages/api/src/router/icons.ts @@ -16,7 +16,7 @@ export const iconsRouter = createTRPCRouter({ url: true, }, where: (input.searchText?.length ?? 0) > 0 ? like(icons.name, `%${input.searchText}%`) : undefined, - limit: 5, + limit: input.limitPerGroup, }, }, }), diff --git a/packages/integrations/src/media-organizer/radarr/radarr-integration.ts b/packages/integrations/src/media-organizer/radarr/radarr-integration.ts index a562ce2d0..bee8b1d63 100644 --- a/packages/integrations/src/media-organizer/radarr/radarr-integration.ts +++ b/packages/integrations/src/media-organizer/radarr/radarr-integration.ts @@ -76,7 +76,7 @@ export class RadarrIntegration extends Integration { name: "IMDb", color: "#f5c518", isDark: false, - logo: "/images/apps/imdb.png", + logo: "/images/apps/imdb.svg", }); } diff --git a/packages/integrations/src/media-organizer/sonarr/sonarr-integration.ts b/packages/integrations/src/media-organizer/sonarr/sonarr-integration.ts index 0b18e2b0a..faa789b1b 100644 --- a/packages/integrations/src/media-organizer/sonarr/sonarr-integration.ts +++ b/packages/integrations/src/media-organizer/sonarr/sonarr-integration.ts @@ -75,7 +75,7 @@ export class SonarrIntegration extends Integration { name: "IMDb", color: "#f5c518", isDark: false, - logo: "/images/apps/imdb.png", + logo: "/images/apps/imdb.svg", }); } diff --git a/packages/validation/src/icons.ts b/packages/validation/src/icons.ts index 031878b99..c621ecea5 100644 --- a/packages/validation/src/icons.ts +++ b/packages/validation/src/icons.ts @@ -2,6 +2,7 @@ import { z } from "zod"; const findIconsSchema = z.object({ searchText: z.string().optional(), + limitPerGroup: z.number().min(1).max(500).default(12), }); export const iconsSchemas = {