Advancement in the new spotlight
This commit is contained in:
@@ -1,8 +1,21 @@
|
|||||||
import { Button, Group } from '@mantine/core';
|
import {
|
||||||
import { IconSearch, IconBrandYoutube, IconDownload, IconMovie } from '@tabler/icons';
|
ActionIcon,
|
||||||
import { SpotlightProvider, openSpotlight } from '@mantine/spotlight';
|
Group,
|
||||||
import type { SpotlightAction } from '@mantine/spotlight';
|
Kbd,
|
||||||
import { useState } from 'react';
|
Select,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import {
|
||||||
|
IconSearch,
|
||||||
|
IconBrandYoutube,
|
||||||
|
IconDownload,
|
||||||
|
IconMovie,
|
||||||
|
IconBrandGoogle,
|
||||||
|
} from '@tabler/icons';
|
||||||
|
import { forwardRef, useState } from 'react';
|
||||||
|
import { useHotkeys } from '@mantine/hooks';
|
||||||
|
import { showNotification } from '@mantine/notifications';
|
||||||
import { IModule } from '../ModuleTypes';
|
import { IModule } from '../ModuleTypes';
|
||||||
|
|
||||||
export const SpotlightModule: IModule = {
|
export const SpotlightModule: IModule = {
|
||||||
@@ -12,95 +25,157 @@ export const SpotlightModule: IModule = {
|
|||||||
id: 'spotlight',
|
id: 'spotlight',
|
||||||
};
|
};
|
||||||
|
|
||||||
interface SearchEngine {
|
const searchEngines = [
|
||||||
name: string;
|
|
||||||
enabled: boolean;
|
|
||||||
description: string;
|
|
||||||
icon: React.ReactNode;
|
|
||||||
url: string;
|
|
||||||
shortcut: string;
|
|
||||||
}
|
|
||||||
const searchEngines: SearchEngine[] = [
|
|
||||||
{
|
{
|
||||||
name: 'Google',
|
label: 'Google',
|
||||||
enabled: true,
|
disabled: false,
|
||||||
description: 'Search using your search engine (defined in settings)',
|
description: 'Search using your search engine (defined in settings)',
|
||||||
icon: <IconSearch />,
|
icon: <IconSearch />,
|
||||||
url: 'https://www.google.com/search?q=',
|
url: 'https://www.google.com/search?q=',
|
||||||
shortcut: 'g',
|
shortcut: 'g',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Youtube',
|
label: 'Youtube',
|
||||||
enabled: true,
|
disabled: false,
|
||||||
description: 'Search Youtube',
|
description: 'Search Youtube',
|
||||||
icon: <IconBrandYoutube />,
|
icon: <IconBrandYoutube />,
|
||||||
url: 'https://www.youtube.com/results?search_query=',
|
url: 'https://www.youtube.com/results?search_query=',
|
||||||
shortcut: 'y',
|
shortcut: 'y',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Download',
|
label: 'Download',
|
||||||
enabled: true,
|
disabled: false,
|
||||||
description: 'Search for torrents',
|
description: 'Search for torrents',
|
||||||
icon: <IconDownload />,
|
icon: <IconDownload />,
|
||||||
url: 'https://1337x.to/search/',
|
url: 'https://1337x.to/search/',
|
||||||
shortcut: 't',
|
shortcut: 't',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Movies',
|
label: 'Movies',
|
||||||
|
disabled: true,
|
||||||
|
description: 'Search for movies using Overseerr',
|
||||||
icon: <IconMovie />,
|
icon: <IconMovie />,
|
||||||
enabled: false,
|
url: 'https://www.imdb.com/find?q=',
|
||||||
|
shortcut: 'm',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const data: ItemProps[] = [
|
||||||
|
{
|
||||||
|
icon: <IconBrandGoogle />,
|
||||||
|
disabled: false,
|
||||||
|
label: 'Google',
|
||||||
|
value: 'google',
|
||||||
|
description: 'Search using your search engine (defined in settings)',
|
||||||
|
url: 'https://www.google.com/search?q=',
|
||||||
|
shortcut: 'g',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
icon: <IconDownload />,
|
||||||
|
disabled: false,
|
||||||
|
label: 'Download',
|
||||||
|
value: 'download',
|
||||||
|
description: 'Search for torrents',
|
||||||
|
url: 'https://1337x.to/search/',
|
||||||
|
shortcut: 't',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconBrandYoutube />,
|
||||||
|
disabled: false,
|
||||||
|
label: 'Youtube',
|
||||||
|
value: 'youtube',
|
||||||
|
description: 'Search Youtube',
|
||||||
|
url: 'https://www.youtube.com/results?search_query=',
|
||||||
|
shortcut: 'y',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconMovie />,
|
||||||
|
disabled: true,
|
||||||
|
label: 'Movies',
|
||||||
|
value: 'movies',
|
||||||
description: 'Search for movies using Overseerr',
|
description: 'Search for movies using Overseerr',
|
||||||
url: 'https://www.imdb.com/find?q=',
|
url: 'https://www.imdb.com/find?q=',
|
||||||
shortcut: 'm',
|
shortcut: 'm',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function SpotlightControl() {
|
interface ItemProps extends React.ComponentPropsWithoutRef<'div'> {
|
||||||
|
label: string;
|
||||||
|
disabled: boolean;
|
||||||
|
value: string;
|
||||||
|
description: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
url: string;
|
||||||
|
shortcut: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SpotlightModuleComponent(props: any) {
|
||||||
|
const [selectedSearchEngine, setSearchEngine] = useState<ItemProps>(data[0]);
|
||||||
|
|
||||||
|
const SelectItem = forwardRef<HTMLDivElement, ItemProps>(
|
||||||
|
({ icon, label, description, shortcut, ...others }: ItemProps, ref) => (
|
||||||
|
<div ref={ref} {...others}>
|
||||||
|
<Group position="apart" noWrap>
|
||||||
|
<Group noWrap>
|
||||||
|
{icon}
|
||||||
|
<div>
|
||||||
|
<Text size="sm">{label}</Text>
|
||||||
|
<Text size="xs" opacity={0.65}>
|
||||||
|
{description}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Group>
|
||||||
|
<Kbd>{shortcut}</Kbd>
|
||||||
|
</Group>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const [opened, setOpened] = useState(false);
|
||||||
|
useHotkeys([['mod+K', () => setOpened(!opened)]]);
|
||||||
return (
|
return (
|
||||||
<Group position="center">
|
<Group grow>
|
||||||
<Button onClick={() => openSpotlight()}>Open spotlight</Button>
|
<Select
|
||||||
|
icon={selectedSearchEngine.icon}
|
||||||
|
onSearchChange={(search) =>
|
||||||
|
setSearchEngine(
|
||||||
|
data.find((item) => item.label.toLowerCase().includes(search.toLowerCase())) ||
|
||||||
|
selectedSearchEngine
|
||||||
|
)
|
||||||
|
}
|
||||||
|
withinPortal
|
||||||
|
defaultValue={selectedSearchEngine.value}
|
||||||
|
itemComponent={SelectItem}
|
||||||
|
data={data}
|
||||||
|
maxDropdownHeight={400}
|
||||||
|
/>
|
||||||
|
<TextInput />
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SpotlightModuleComponent(props: any) {
|
function tryMatchSearch(
|
||||||
const [selectedSearchEngine, setSearchEngine] = useState<SearchEngine>(searchEngines[0]);
|
query: string,
|
||||||
const actions: SpotlightAction[] = searchEngines.map((engine) => ({
|
selectedSearchEngine: ItemProps,
|
||||||
title: engine.name,
|
setSearchEngine: React.Dispatch<React.SetStateAction<ItemProps>>,
|
||||||
description: engine.description,
|
searchEngines: ItemProps[]
|
||||||
icon: engine.icon,
|
|
||||||
onTrigger: () => {
|
|
||||||
setSearchEngine(engine);
|
|
||||||
},
|
|
||||||
closeOnTrigger: false,
|
|
||||||
}));
|
|
||||||
return (
|
|
||||||
<SpotlightProvider
|
|
||||||
actions={actions}
|
|
||||||
searchIcon={selectedSearchEngine.icon}
|
|
||||||
searchPlaceholder={selectedSearchEngine.description}
|
|
||||||
shortcut="mod + k"
|
|
||||||
nothingFoundMessage="Press enter to search..."
|
|
||||||
onQueryChange={(researchString) =>
|
|
||||||
useSearchBrowser(researchString, selectedSearchEngine, setSearchEngine, searchEngines)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SpotlightControl />
|
|
||||||
</SpotlightProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
function useSearchBrowser(
|
|
||||||
search: string,
|
|
||||||
selectedSearchEngine: SearchEngine,
|
|
||||||
setSearchEngine: React.Dispatch<React.SetStateAction<SearchEngine>>,
|
|
||||||
searchEngines: SearchEngine[]
|
|
||||||
): void {
|
): void {
|
||||||
// First check if the value of search contains any shortcut
|
// First check if the value of search contains any shortcut. Make sure it is not disabled
|
||||||
const foundSearchEngine = searchEngines.find((engine) => search.includes(`!${engine.shortcut}`));
|
const foundSearchEngine = searchEngines.find(
|
||||||
|
(engine) => query.includes(`!${engine.shortcut}`) && !engine.disabled
|
||||||
|
);
|
||||||
if (foundSearchEngine) {
|
if (foundSearchEngine) {
|
||||||
// If a shortcut is found, use it
|
// If a shortcut is found, use it. Except if it is disabled
|
||||||
setSearchEngine(foundSearchEngine);
|
setSearchEngine(foundSearchEngine);
|
||||||
search.replace(`!${foundSearchEngine.shortcut}`, '');
|
showNotification({
|
||||||
|
radius: 'lg',
|
||||||
|
disallowClose: true,
|
||||||
|
id: 'spotlight',
|
||||||
|
autoClose: 1000,
|
||||||
|
icon: <ActionIcon size="sm">{foundSearchEngine.icon}</ActionIcon>,
|
||||||
|
message: `Using ${foundSearchEngine.label} as search engine`,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// If no shortcut is found, do nothing (for now)
|
// If no shortcut is found, do nothing (for now)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user