Search Provider icons
This commit is contained in:
@@ -29,23 +29,12 @@ export function AppItem({
|
||||
const { ref, width, height } = useElementSize();
|
||||
const allIcons: any = { ...fa6, ...vsc, ...si };
|
||||
const Icon = allIcons[icon];
|
||||
const { isMobile, theme } = useCommonHooks();
|
||||
const { isMobile, filter } = useCommonHooks();
|
||||
const [textDecoration, setTextDecoration] = useState<string>('none');
|
||||
const [hover, setHover] = useState<boolean>(false);
|
||||
const validUrl =
|
||||
url.startsWith('http://') || url.startsWith('https://') ? url : `http://${url}`;
|
||||
|
||||
const filter = () => {
|
||||
if (selectedTheme && selectedTheme.appShadowsEnabled === 1) {
|
||||
return 'drop-shadow(2px 2px color-mix(in srgb, var(--mantine-color-accent-0), transparent 30%))';
|
||||
}
|
||||
if (!selectedTheme && theme.appShadowsEnabled === 1) {
|
||||
return 'drop-shadow(2px 2px color-mix(in srgb, var(--mantine-color-accent-0), transparent 30%))';
|
||||
}
|
||||
|
||||
return 'none';
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
component='a'
|
||||
@@ -113,7 +102,7 @@ export function AppItem({
|
||||
style={{
|
||||
height: scale * 32,
|
||||
width: scale * 32,
|
||||
filter: filter(),
|
||||
filter: filter(selectedTheme),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -65,16 +65,27 @@ export function useCommonHooks() {
|
||||
key: 'providerPrefix',
|
||||
defaultValue: '/g',
|
||||
});
|
||||
const [defaultProviderIcon, setDefaultProviderIcon] = useLocalStorage<string>({
|
||||
key: 'providerIcon',
|
||||
defaultValue: 'FaGoogle',
|
||||
});
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [providerLabel, setProviderLabel] = useState(defaultProviderLabel);
|
||||
const [providerURL, setProviderURL] = useState(defaultProviderURL);
|
||||
const [providerPrefix, setProviderPrefix] = useState(defaultProviderPrefix);
|
||||
const [providerIcon, setProviderIcon] = useState(defaultProviderIcon);
|
||||
|
||||
useEffect(() => {
|
||||
setProviderLabel(defaultProviderLabel);
|
||||
setProviderURL(defaultProviderURL);
|
||||
setProviderPrefix(defaultProviderPrefix);
|
||||
}, [defaultProviderLabel, defaultProviderURL, defaultProviderPrefix]);
|
||||
setProviderIcon(defaultProviderIcon);
|
||||
}, [
|
||||
defaultProviderLabel,
|
||||
defaultProviderURL,
|
||||
defaultProviderPrefix,
|
||||
defaultProviderIcon,
|
||||
]);
|
||||
|
||||
const [currentUser, setCurrentUser] = useLocalStorage<User>({
|
||||
key: 'user',
|
||||
@@ -141,6 +152,17 @@ export function useCommonHooks() {
|
||||
mutateAsync({ method: 'DELETE', endpoint: endpoint, body: item });
|
||||
};
|
||||
|
||||
const filter = (selectedTheme?: Theme) => {
|
||||
if (selectedTheme && selectedTheme.appShadowsEnabled === 1) {
|
||||
return 'drop-shadow(2px 2px color-mix(in srgb, var(--mantine-color-accent-0), transparent 30%))';
|
||||
}
|
||||
if (!selectedTheme && theme.appShadowsEnabled === 1) {
|
||||
return 'drop-shadow(2px 2px color-mix(in srgb, var(--mantine-color-accent-0), transparent 30%))';
|
||||
}
|
||||
|
||||
return 'none';
|
||||
};
|
||||
|
||||
function fixDigits(k: number) {
|
||||
if (k < 10) {
|
||||
return `0${k}`;
|
||||
@@ -195,6 +217,7 @@ export function useCommonHooks() {
|
||||
theme,
|
||||
setTheme,
|
||||
urlPattern,
|
||||
filter,
|
||||
fixDigits,
|
||||
reorder,
|
||||
getObjectDiff,
|
||||
@@ -230,12 +253,16 @@ export function useCommonHooks() {
|
||||
setProviderURL,
|
||||
providerPrefix,
|
||||
setProviderPrefix,
|
||||
providerIcon,
|
||||
setProviderIcon,
|
||||
defaultProviderLabel,
|
||||
setDefaultProviderLabel,
|
||||
defaultProviderURL,
|
||||
setDefaultProviderURL,
|
||||
defaultProviderPrefix,
|
||||
setDefaultProviderPrefix,
|
||||
defaultProviderIcon,
|
||||
setDefaultProviderIcon,
|
||||
currentUser,
|
||||
setCurrentUser,
|
||||
currentLocation,
|
||||
|
||||
@@ -81,6 +81,8 @@ export default function Search({
|
||||
setProviderURL,
|
||||
providerPrefix,
|
||||
setProviderPrefix,
|
||||
providerIcon,
|
||||
setProviderIcon,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
defaultProviderURL,
|
||||
@@ -89,10 +91,13 @@ export default function Search({
|
||||
setDefaultProviderLabel,
|
||||
defaultProviderPrefix,
|
||||
setDefaultProviderPrefix,
|
||||
defaultProviderIcon,
|
||||
setDefaultProviderIcon,
|
||||
} = useCommonHooks();
|
||||
const [placeholderValue, setPlaceholderValue] = useState<string>('');
|
||||
const [value, setValue] = useState(0);
|
||||
const [historyMultiplier, setHistoryMultiplier] = useState(1);
|
||||
const Icon = allIcons[providerIcon];
|
||||
|
||||
const handleResetSearch = () => {
|
||||
setSearchValue('');
|
||||
@@ -259,6 +264,7 @@ export default function Search({
|
||||
setProviderLabel('Google Search');
|
||||
setProviderURL('https://www.google.com/search?q=');
|
||||
setProviderPrefix('/g');
|
||||
setProviderIcon('FaGoogle');
|
||||
} else if (item.target.value.startsWith('/')) {
|
||||
const provider = providers?.find(
|
||||
(p: Provider) => p.prefix === item.target.value.split(' ')[0],
|
||||
@@ -266,6 +272,7 @@ export default function Search({
|
||||
setProviderLabel(provider?.name ?? 'Invalid Search');
|
||||
setProviderURL(provider?.url ?? '');
|
||||
setProviderPrefix(provider?.prefix ?? '');
|
||||
setProviderIcon(provider?.icon ?? '');
|
||||
} else if (item.target.value.startsWith('!')) {
|
||||
if (item.target.value === '!w' || item.target.value === '!weather') {
|
||||
setProviderLabel('Show weather');
|
||||
@@ -283,6 +290,7 @@ export default function Search({
|
||||
setProviderLabel(defaultProviderLabel);
|
||||
setProviderURL(defaultProviderURL);
|
||||
setProviderPrefix(defaultProviderPrefix);
|
||||
setProviderIcon(defaultProviderIcon);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -333,7 +341,12 @@ export default function Search({
|
||||
};
|
||||
|
||||
const renderItem: any = (item: any) => {
|
||||
const Icon = allIcons[item.option.icon];
|
||||
const Icon =
|
||||
allIcons[
|
||||
(item.option.icon ?? item.option.prefix === '/g')
|
||||
? 'FaGoogle'
|
||||
: (providers.find((p: Provider) => p.prefix === item.option.prefix)?.icon ?? '')
|
||||
];
|
||||
return !searchValue.startsWith('!') ? (
|
||||
item.option.value === 'load' ? (
|
||||
<Group
|
||||
@@ -363,6 +376,7 @@ export default function Search({
|
||||
handleSearchHistoryItem={handleSearchHistoryItem}
|
||||
searchEndpoint={searchEndpoint}
|
||||
setSearchValue={setSearchValue}
|
||||
Icon={Icon}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
@@ -470,6 +484,7 @@ export default function Search({
|
||||
);
|
||||
setDefaultProviderPrefix(defaultProviderData[0]?.prefix ?? '/g');
|
||||
setDefaultProviderLabel(defaultProviderData[0]?.name ?? 'Google Search');
|
||||
setDefaultProviderIcon(defaultProviderData[0]?.icon ?? 'FaGoogle');
|
||||
}
|
||||
}, [defaultProviderData, defaultProviderDataIsFetched]);
|
||||
|
||||
@@ -520,7 +535,18 @@ export default function Search({
|
||||
spellCheck={true}
|
||||
searchable
|
||||
autoFocus={!isMobile}
|
||||
leftSection={providerLabel}
|
||||
leftSection={
|
||||
providerIcon ? (
|
||||
<Icon
|
||||
style={{
|
||||
height: 32,
|
||||
width: 32,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
providerLabel
|
||||
)
|
||||
}
|
||||
rightSection={<div />}
|
||||
clearable
|
||||
onClear={handleResetSearch}
|
||||
|
||||
@@ -9,6 +9,7 @@ function SearchSelectItem({
|
||||
handleSearchHistoryItem,
|
||||
searchEndpoint,
|
||||
setSearchValue,
|
||||
Icon,
|
||||
}: any) {
|
||||
const { isMobile, theme } = useCommonHooks();
|
||||
const { mutateAsync } = useMutationAPI();
|
||||
@@ -22,7 +23,6 @@ function SearchSelectItem({
|
||||
const dateString = isMobile
|
||||
? `${month}/${day}/${year}`
|
||||
: `${month}/${day}/${year} ${hours}:${minutes}`;
|
||||
|
||||
return (
|
||||
<Group
|
||||
w={'100%'}
|
||||
@@ -39,24 +39,48 @@ function SearchSelectItem({
|
||||
setShowFillButton(false);
|
||||
}}
|
||||
>
|
||||
<Group m={0} p={0} w={'160px'}>
|
||||
<Group
|
||||
m={0}
|
||||
p={0}
|
||||
w={'120px'}
|
||||
justify='center'
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{Icon ? (
|
||||
<Icon
|
||||
style={{
|
||||
height: 32,
|
||||
width: 32,
|
||||
backgroundColor: `${theme.primaryColor}33`,
|
||||
borderRadius: '15px',
|
||||
padding: '.25rem',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Text
|
||||
style={{
|
||||
backgroundColor: `${theme.primaryColor}33`,
|
||||
fontWeight: 600,
|
||||
borderRadius: '15px',
|
||||
padding: '.25rem',
|
||||
marginRight: '.25rem',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{item.option.provider}
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
<Group
|
||||
m={0}
|
||||
p={0}
|
||||
gap={0}
|
||||
justify='space-between'
|
||||
w={'calc(100% - 160px)'}
|
||||
w={'calc(100% - 120px)'}
|
||||
style={{
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
import { Box, Card, Group, Stack, Text, TextInput } from '@mantine/core';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
type ComboboxItem,
|
||||
type ComboboxLikeRenderOptionInput,
|
||||
Group,
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
} from '@mantine/core';
|
||||
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
||||
import { DeleteItemModal, ManagementPill } from 'components';
|
||||
import { useCommonHooks, useMutationAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { cardClasses, textClasses, textInputClasses } from 'styles';
|
||||
import { cardClasses, selectClasses, textClasses, textInputClasses } from 'styles';
|
||||
import type { Provider } from 'types';
|
||||
|
||||
function DebouncedSearchProvider({
|
||||
searchProvider,
|
||||
setIsOpen,
|
||||
allIcons,
|
||||
}: {
|
||||
searchProvider: Provider;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
allIcons: any;
|
||||
}) {
|
||||
const { handleDelete, isMobile, theme } = useCommonHooks();
|
||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||
@@ -81,7 +93,7 @@ function DebouncedSearchProvider({
|
||||
</Box>
|
||||
|
||||
<Stack w={'100%'} align='center'>
|
||||
<Group w={'100%'} justify='center'>
|
||||
<Group w={'100%'} justify='center' pos={'relative'}>
|
||||
<TextInput
|
||||
label={'Name'}
|
||||
className={textInputClasses.settingsItem}
|
||||
@@ -100,6 +112,7 @@ function DebouncedSearchProvider({
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
w={'30%'}
|
||||
/>
|
||||
<TextInput
|
||||
label={'Prefix'}
|
||||
@@ -119,11 +132,63 @@ function DebouncedSearchProvider({
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
w={'30%'}
|
||||
/>
|
||||
<Select
|
||||
label={'Icon'}
|
||||
className={selectClasses.settingsItem}
|
||||
classNames={{
|
||||
wrapper: selectClasses.menuSelectWrapper,
|
||||
input: selectClasses.menuSelectInput,
|
||||
label: selectClasses.menuSelectLabel,
|
||||
options: selectClasses.menuSelectOptions,
|
||||
option: selectClasses.menuSelectOption,
|
||||
dropdown: selectClasses.dropdown,
|
||||
}}
|
||||
w={'30%'}
|
||||
mt={'.75rem'}
|
||||
limit={100}
|
||||
placeholder='Search for an icon...'
|
||||
defaultSearchValue={provider?.icon ?? ''}
|
||||
defaultValue={provider?.icon}
|
||||
data={Object.keys(allIcons).map((icon) => {
|
||||
return { value: icon, label: icon };
|
||||
})}
|
||||
comboboxProps={{
|
||||
zIndex: 3000,
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onChange={(value: string | null) => {
|
||||
setUpdate({
|
||||
...provider,
|
||||
icon: value,
|
||||
});
|
||||
}}
|
||||
renderOption={(item: ComboboxLikeRenderOptionInput<ComboboxItem>) => {
|
||||
const Icon = allIcons[item.option.value];
|
||||
return (
|
||||
<Group>
|
||||
{item.option.value}
|
||||
<Icon />
|
||||
</Group>
|
||||
);
|
||||
}}
|
||||
searchable
|
||||
clearable
|
||||
maxDropdownHeight={175}
|
||||
onDropdownOpen={() => setIsOpen(true)}
|
||||
onDropdownClose={() => setIsOpen(false)}
|
||||
/>
|
||||
</Group>
|
||||
<TextInput
|
||||
label={'Url'}
|
||||
w={isMobile ? '80%' : '100%'}
|
||||
w={isMobile ? '80%' : '94%'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={provider.url}
|
||||
|
||||
@@ -1,11 +1,42 @@
|
||||
import { Card, isLightColor, Stack, Text } from '@mantine/core';
|
||||
import { Box, Card, isLightColor, Stack, Text } from '@mantine/core';
|
||||
import HintItem from 'components/HelpModal/HintItem';
|
||||
import { cardClasses } from 'styles';
|
||||
import type { Provider } from 'types';
|
||||
|
||||
function SearchProviderSettingsItem({ provider }: { provider: Provider }) {
|
||||
function SearchProviderSettingsItem({
|
||||
provider,
|
||||
Icon,
|
||||
filter,
|
||||
}: {
|
||||
provider: Provider;
|
||||
Icon: any;
|
||||
filter: any;
|
||||
}) {
|
||||
return (
|
||||
<Card className={cardClasses.settingsItemHover} classNames={cardClasses}>
|
||||
{Icon && (
|
||||
<Box
|
||||
pos={'absolute'}
|
||||
top={0}
|
||||
left={0}
|
||||
style={{
|
||||
color: 'var(--mantine-color-primary-0)',
|
||||
backgroundColor: 'var(--mantine-color-accent-0',
|
||||
padding: '0.5rem',
|
||||
borderBottomRightRadius: '.5rem',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
style={{
|
||||
height: 32,
|
||||
width: 32,
|
||||
filter: filter(),
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Stack
|
||||
gap={0}
|
||||
style={{
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
type ComboboxItem,
|
||||
type ComboboxLikeRenderOptionInput,
|
||||
Grid,
|
||||
GridCol,
|
||||
Group,
|
||||
Popover,
|
||||
Select,
|
||||
Stack,
|
||||
Switch,
|
||||
TextInput,
|
||||
@@ -15,13 +18,23 @@ import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
||||
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
|
||||
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import * as fa6 from 'react-icons/fa6';
|
||||
import { FiPower } from 'react-icons/fi';
|
||||
import { popoverClasses, switchClasses, textInputClasses, toolTipClasses } from 'styles';
|
||||
import * as si from 'react-icons/si';
|
||||
import * as vsc from 'react-icons/vsc';
|
||||
import {
|
||||
popoverClasses,
|
||||
selectClasses,
|
||||
switchClasses,
|
||||
textInputClasses,
|
||||
toolTipClasses,
|
||||
} from 'styles';
|
||||
import type { Provider } from 'types';
|
||||
import DebouncedSearchProvider from './DebouncedSearchProvider';
|
||||
import SearchProviderSettingsItem from './SearchProviderSettingsItem';
|
||||
|
||||
const SearchProviderSettings = () => {
|
||||
const allIcons: any = { ...fa6, ...vsc, ...si };
|
||||
const defaultProvider = {
|
||||
id: undefined,
|
||||
name: '',
|
||||
@@ -42,7 +55,7 @@ const SearchProviderSettings = () => {
|
||||
endpoint: endpoint,
|
||||
});
|
||||
const [providers, setProviders] = useState<Provider[]>([]);
|
||||
const { isMobile, theme } = useCommonHooks();
|
||||
const { isMobile, theme, filter } = useCommonHooks();
|
||||
const { mutateAsync } = useMutationAPI();
|
||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
@@ -91,7 +104,7 @@ const SearchProviderSettings = () => {
|
||||
>
|
||||
<Stack gap={0} w={'100%'} align='center'>
|
||||
<Stack w={'100%'} align='center'>
|
||||
<Group w={'100%'} justify='center'>
|
||||
<Group w={'100%'} justify='center' pos={'relative'}>
|
||||
<TextInput
|
||||
key={form.key('name')}
|
||||
{...form.getInputProps('name')}
|
||||
@@ -107,6 +120,7 @@ const SearchProviderSettings = () => {
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
w={'30%'}
|
||||
/>
|
||||
<TextInput
|
||||
key={form.key('prefix')}
|
||||
@@ -123,6 +137,52 @@ const SearchProviderSettings = () => {
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
w={'30%'}
|
||||
/>
|
||||
<Select
|
||||
key={form.key('icon')}
|
||||
{...form.getInputProps('icon')}
|
||||
label={'Icon'}
|
||||
className={selectClasses.settingsItem}
|
||||
classNames={{
|
||||
wrapper: selectClasses.menuSelectWrapper,
|
||||
input: selectClasses.menuSelectInput,
|
||||
label: selectClasses.menuSelectLabel,
|
||||
options: selectClasses.menuSelectOptions,
|
||||
option: selectClasses.menuSelectOption,
|
||||
dropdown: selectClasses.dropdown,
|
||||
}}
|
||||
w={'30%'}
|
||||
mt={'.75rem'}
|
||||
limit={100}
|
||||
placeholder='Search for an icon...'
|
||||
data={Object.keys(allIcons).map((icon) => {
|
||||
return { value: icon, label: icon };
|
||||
})}
|
||||
comboboxProps={{
|
||||
zIndex: 3000,
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
renderOption={(item: ComboboxLikeRenderOptionInput<ComboboxItem>) => {
|
||||
const Icon = allIcons[item.option.value];
|
||||
return (
|
||||
<Group>
|
||||
{item.option.value}
|
||||
<Icon />
|
||||
</Group>
|
||||
);
|
||||
}}
|
||||
searchable
|
||||
clearable
|
||||
maxDropdownHeight={175}
|
||||
onDropdownOpen={() => setIsOpen(true)}
|
||||
onDropdownClose={() => setIsOpen(false)}
|
||||
/>
|
||||
</Group>
|
||||
<TextInput
|
||||
@@ -131,7 +191,7 @@ const SearchProviderSettings = () => {
|
||||
label={'Url'}
|
||||
placeholder='eg. https://www.google.com/search?q='
|
||||
className={textInputClasses.settingsItem}
|
||||
w={'64%'}
|
||||
w={'94%'}
|
||||
classNames={textInputClasses}
|
||||
withAsterisk
|
||||
styles={
|
||||
@@ -228,13 +288,18 @@ const SearchProviderSettings = () => {
|
||||
>
|
||||
<Popover.Target>
|
||||
<Box>
|
||||
<SearchProviderSettingsItem provider={provider} />
|
||||
<SearchProviderSettingsItem
|
||||
provider={provider}
|
||||
Icon={allIcons[provider.icon || '']}
|
||||
filter={filter}
|
||||
/>
|
||||
</Box>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown w={isMobile ? '90%' : undefined}>
|
||||
<DebouncedSearchProvider
|
||||
searchProvider={provider}
|
||||
setIsOpen={setIsOpen}
|
||||
allIcons={allIcons}
|
||||
/>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
|
||||
@@ -4,4 +4,6 @@ export interface Provider {
|
||||
url: string;
|
||||
prefix: string;
|
||||
default: number;
|
||||
enabled: number;
|
||||
icon: string | null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user