mirror of
https://github.com/ajnart/homarr.git
synced 2026-02-28 01:10:54 +01:00
* feat: update prettier configuration for print width * chore: apply code formatting to entire repository * fix: remove build files * fix: format issue --------- Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
127 lines
4.0 KiB
TypeScript
127 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useState } from "react";
|
|
import Link from "next/link";
|
|
import { Center, Chip, Divider, Flex, Group, Text } from "@mantine/core";
|
|
import { Spotlight as MantineSpotlight, SpotlightAction } from "@mantine/spotlight";
|
|
import { IconSearch } from "@tabler/icons-react";
|
|
import { useAtomValue } from "jotai";
|
|
|
|
import type { TranslationFunction } from "@homarr/translation";
|
|
import { useI18n } from "@homarr/translation/client";
|
|
|
|
import { GroupChip } from "./chip-group";
|
|
import classes from "./component.module.css";
|
|
import { actionsAtomRead, groupsAtomRead } from "./data-store";
|
|
import { setSelectedAction, spotlightStore } from "./spotlight-store";
|
|
import type { SpotlightActionData } from "./type";
|
|
import { useWebSearchEngines } from "./web-search-engines";
|
|
|
|
export const Spotlight = () => {
|
|
useWebSearchEngines();
|
|
const [query, setQuery] = useState("");
|
|
const [group, setGroup] = useState("all");
|
|
const groups = useAtomValue(groupsAtomRead);
|
|
const actions = useAtomValue(actionsAtomRead);
|
|
const t = useI18n();
|
|
|
|
const preparedActions = actions.map((action) => prepareAction(action, t));
|
|
const items = preparedActions
|
|
.filter(
|
|
(item) =>
|
|
(item.ignoreSearchAndOnlyShowInGroup
|
|
? item.group === group
|
|
: item.title.toLowerCase().includes(query.toLowerCase().trim())) &&
|
|
(group === "all" || item.group === group),
|
|
)
|
|
.map((item) => {
|
|
const renderRoot =
|
|
item.type === "link"
|
|
? (props: Record<string, unknown>) => (
|
|
<Link href={prepareHref(item.href, query)} target={item.openInNewTab ? "_blank" : undefined} {...props} />
|
|
)
|
|
: undefined;
|
|
|
|
return (
|
|
<SpotlightAction
|
|
key={item.id}
|
|
renderRoot={renderRoot}
|
|
onClick={item.type === "button" ? item.onClick : undefined}
|
|
className={classes.spotlightAction}
|
|
>
|
|
<Group wrap="nowrap" w="100%">
|
|
{item.icon && (
|
|
<Center w={50} h={50}>
|
|
{typeof item.icon !== "string" && <item.icon size={24} />}
|
|
{typeof item.icon === "string" && <img src={item.icon} alt={item.title} width={24} height={24} />}
|
|
</Center>
|
|
)}
|
|
|
|
<Flex direction="column">
|
|
<Text>{item.title}</Text>
|
|
|
|
{item.description && (
|
|
<Text opacity={0.6} size="xs">
|
|
{item.description}
|
|
</Text>
|
|
)}
|
|
</Flex>
|
|
</Group>
|
|
</SpotlightAction>
|
|
);
|
|
});
|
|
|
|
const onGroupChange = useCallback(
|
|
(group: string) => {
|
|
setSelectedAction(-1, spotlightStore);
|
|
setGroup(group);
|
|
},
|
|
[setGroup, setSelectedAction],
|
|
);
|
|
|
|
return (
|
|
<MantineSpotlight.Root query={query} onQueryChange={setQuery} store={spotlightStore}>
|
|
<MantineSpotlight.Search
|
|
placeholder={t("common.rtl", {
|
|
value: t("common.search.placeholder"),
|
|
symbol: "...",
|
|
})}
|
|
leftSection={<IconSearch stroke={1.5} />}
|
|
/>
|
|
|
|
<Divider />
|
|
<Group wrap="nowrap" p="sm">
|
|
<Chip.Group multiple={false} value={group} onChange={onGroupChange}>
|
|
<Group justify="start">
|
|
{groups.map((group) => (
|
|
<GroupChip key={group} group={group} />
|
|
))}
|
|
</Group>
|
|
</Chip.Group>
|
|
</Group>
|
|
|
|
<MantineSpotlight.ActionsList>
|
|
{items.length > 0 ? items : <MantineSpotlight.Empty>{t("common.search.nothingFound")}</MantineSpotlight.Empty>}
|
|
</MantineSpotlight.ActionsList>
|
|
</MantineSpotlight.Root>
|
|
);
|
|
};
|
|
|
|
const prepareHref = (href: string, query: string) => {
|
|
return href.replace("%s", query);
|
|
};
|
|
|
|
const translateIfNecessary = (value: string | ((t: TranslationFunction) => string), t: TranslationFunction) => {
|
|
if (typeof value === "function") {
|
|
return value(t);
|
|
}
|
|
|
|
return value;
|
|
};
|
|
|
|
const prepareAction = (action: SpotlightActionData, t: TranslationFunction) => ({
|
|
...action,
|
|
title: translateIfNecessary(action.title, t),
|
|
description: translateIfNecessary(action.description, t),
|
|
});
|