refactor: use tabs instead of spaces

This commit is contained in:
Sonny
2024-10-07 01:33:59 +02:00
parent f425decf2c
commit eea9732100
197 changed files with 5206 additions and 5209 deletions

View File

@@ -3,27 +3,27 @@ import { useTranslation } from 'react-i18next';
import { FcGoogle } from 'react-icons/fc';
const NoSearchResultStyle = styled.i({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25em',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25em',
'& > span': {
display: 'flex',
alignItems: 'center',
},
'& > span': {
display: 'flex',
alignItems: 'center',
},
});
export default function NoSearchResult() {
const { t } = useTranslation('common');
return (
<NoSearchResultStyle>
{t('search-with')}
{''}
<span>
<FcGoogle size={20} />
oogle
</span>
</NoSearchResultStyle>
);
const { t } = useTranslation('common');
return (
<NoSearchResultStyle>
{t('search-with')}
{''}
<span>
<FcGoogle size={20} />
oogle
</span>
</NoSearchResultStyle>
);
}

View File

@@ -15,142 +15,142 @@ import { makeRequest } from '~/lib/request';
import { SearchResult } from '~/types/search';
const SearchInput = styled.input(({ theme }) => ({
width: '100%',
fontSize: '20px',
color: theme.colors.font,
backgroundColor: 'transparent',
paddingLeft: 0,
border: '1px solid transparent',
width: '100%',
fontSize: '20px',
color: theme.colors.font,
backgroundColor: 'transparent',
paddingLeft: 0,
border: '1px solid transparent',
}));
interface SearchModalProps {
openItem: any;
openItem: any;
}
function SearchModal({ openItem: OpenItem }: SearchModalProps) {
const { t } = useTranslation();
const { collections } = useCollections();
const { setActiveCollection } = useActiveCollection();
const { t } = useTranslation();
const { collections } = useCollections();
const { setActiveCollection } = useActiveCollection();
const [searchTerm, setSearchTerm] = useState<string>('');
const [results, setResults] = useState<SearchResult[]>([]);
const [selectedItem, setSelectedItem] = useState<SearchResult | null>(null);
const [searchTerm, setSearchTerm] = useState<string>('');
const [results, setResults] = useState<SearchResult[]>([]);
const [selectedItem, setSelectedItem] = useState<SearchResult | null>(null);
const searchModal = useToggle(!!searchTerm);
const searchModal = useToggle(!!searchTerm);
const handleCloseModal = useCallback(() => {
searchModal.close();
setSearchTerm('');
}, [searchModal]);
const handleCloseModal = useCallback(() => {
searchModal.close();
setSearchTerm('');
}, [searchModal]);
const handleSearchInputChange = (value: string) => setSearchTerm(value);
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
handleCloseModal();
const handleSearchInputChange = (value: string) => setSearchTerm(value);
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
handleCloseModal();
if (results.length === 0) {
return window.open(GOOGLE_SEARCH_URL + encodeURI(searchTerm.trim()));
}
if (results.length === 0) {
return window.open(GOOGLE_SEARCH_URL + encodeURI(searchTerm.trim()));
}
if (!selectedItem) return;
if (!selectedItem) return;
if (selectedItem.type === 'collection') {
const collection = collections.find((c) => c.id === selectedItem.id);
if (collection) {
setActiveCollection(collection);
}
return;
}
if (selectedItem.type === 'collection') {
const collection = collections.find((c) => c.id === selectedItem.id);
if (collection) {
setActiveCollection(collection);
}
return;
}
window.open(selectedItem.url);
};
window.open(selectedItem.url);
};
useShortcut('OPEN_SEARCH_KEY', searchModal.open, {
enabled: !searchModal.isShowing,
});
useShortcut('ESCAPE_KEY', handleCloseModal, {
enabled: searchModal.isShowing,
disableGlobalCheck: true,
});
useShortcut('OPEN_SEARCH_KEY', searchModal.open, {
enabled: !searchModal.isShowing,
});
useShortcut('ESCAPE_KEY', handleCloseModal, {
enabled: searchModal.isShowing,
disableGlobalCheck: true,
});
useEffect(() => {
if (searchTerm.trim() === '') {
return setResults([]);
}
useEffect(() => {
if (searchTerm.trim() === '') {
return setResults([]);
}
const controller = new AbortController();
const { url, method } = route('search', { qs: { term: searchTerm } });
makeRequest({
method,
url,
controller,
}).then(({ results: _results }) => {
setResults(_results);
setSelectedItem(_results?.[0]);
});
const controller = new AbortController();
const { url, method } = route('search', { qs: { term: searchTerm } });
makeRequest({
method,
url,
controller,
}).then(({ results: _results }) => {
setResults(_results);
setSelectedItem(_results?.[0]);
});
return () => controller.abort();
}, [searchTerm]);
return () => controller.abort();
}, [searchTerm]);
return (
<>
<OpenItem onClick={searchModal.open}>
<IoIosSearch /> {t('common:search')}
</OpenItem>
<Modal
close={handleCloseModal}
opened={searchModal.isShowing}
hideCloseBtn
css={{ width: '650px' }}
>
<form
onSubmit={handleSubmit}
css={{
width: '100%',
display: 'flex',
gap: '0.5em',
flexDirection: 'column',
}}
>
<div
css={{
display: 'flex',
gap: '0.35em',
alignItems: 'center',
justifyContent: 'center',
}}
>
<label htmlFor="search" css={{ display: 'flex' }}>
<IoIosSearch size={24} />
</label>
<SearchInput
name="search"
id="search"
onChange={({ target }) => handleSearchInputChange(target.value)}
value={searchTerm}
placeholder={t('common:search')}
autoFocus
/>
</div>
{results.length > 0 && selectedItem && (
<SearchResultList
results={results}
selectedItem={selectedItem}
setSelectedItem={setSelectedItem}
/>
)}
{results.length === 0 && !!searchTerm.trim() && <NoSearchResult />}
<button
type="submit"
disabled={searchTerm.length === 0}
style={{ display: 'none' }}
>
{t('common:confirm')}
</button>
</form>
</Modal>
</>
);
return (
<>
<OpenItem onClick={searchModal.open}>
<IoIosSearch /> {t('common:search')}
</OpenItem>
<Modal
close={handleCloseModal}
opened={searchModal.isShowing}
hideCloseBtn
css={{ width: '650px' }}
>
<form
onSubmit={handleSubmit}
css={{
width: '100%',
display: 'flex',
gap: '0.5em',
flexDirection: 'column',
}}
>
<div
css={{
display: 'flex',
gap: '0.35em',
alignItems: 'center',
justifyContent: 'center',
}}
>
<label htmlFor="search" css={{ display: 'flex' }}>
<IoIosSearch size={24} />
</label>
<SearchInput
name="search"
id="search"
onChange={({ target }) => handleSearchInputChange(target.value)}
value={searchTerm}
placeholder={t('common:search')}
autoFocus
/>
</div>
{results.length > 0 && selectedItem && (
<SearchResultList
results={results}
selectedItem={selectedItem}
setSelectedItem={setSelectedItem}
/>
)}
{results.length === 0 && !!searchTerm.trim() && <NoSearchResult />}
<button
type="submit"
disabled={searchTerm.length === 0}
style={{ display: 'none' }}
>
{t('common:confirm')}
</button>
</form>
</Modal>
</>
);
}
export default SearchModal;

View File

@@ -6,118 +6,118 @@ import TextEllipsis from '~/components/common/text_ellipsis';
import LinkFavicon from '~/components/dashboard/link/link_favicon';
import useCollections from '~/hooks/use_collections';
import {
SearchResult,
SearchResultCollection,
SearchResultLink,
SearchResult,
SearchResultCollection,
SearchResultLink,
} from '~/types/search';
const SearchItemStyle = styled('li', {
shouldForwardProp: (propName) => propName !== 'isActive',
shouldForwardProp: (propName) => propName !== 'isActive',
})<{ isActive: boolean }>(({ theme, isActive }) => ({
fontSize: '16px',
backgroundColor: isActive ? theme.colors.secondary : 'transparent',
display: 'flex',
gap: '0.35em',
alignItems: 'center',
borderRadius: theme.border.radius,
padding: '0.25em 0.35em !important',
fontSize: '16px',
backgroundColor: isActive ? theme.colors.secondary : 'transparent',
display: 'flex',
gap: '0.35em',
alignItems: 'center',
borderRadius: theme.border.radius,
padding: '0.25em 0.35em !important',
}));
interface CommonResultProps {
innerRef: RefObject<HTMLLIElement>;
isActive: boolean;
onMouseEnter: () => void;
onMouseLeave: () => void;
innerRef: RefObject<HTMLLIElement>;
isActive: boolean;
onMouseEnter: () => void;
onMouseLeave: () => void;
}
export default function SearchResultItem({
result,
isActive,
onHover,
result,
isActive,
onHover,
}: {
result: SearchResult;
isActive: boolean;
onHover: (result: SearchResult) => void;
result: SearchResult;
isActive: boolean;
onHover: (result: SearchResult) => void;
}) {
const itemRef = useRef<HTMLLIElement>(null);
const [isHovering, setHovering] = useState<boolean>(false);
const itemRef = useRef<HTMLLIElement>(null);
const [isHovering, setHovering] = useState<boolean>(false);
const onMouseEnter = () => {
if (!isHovering) {
onHover(result);
setHovering(true);
}
};
const onMouseLeave = () => setHovering(false);
const onMouseEnter = () => {
if (!isHovering) {
onHover(result);
setHovering(true);
}
};
const onMouseLeave = () => setHovering(false);
useEffect(() => {
if (isActive && !isHovering) {
itemRef.current?.scrollIntoView({
behavior: 'instant',
block: 'nearest',
});
}
}, [itemRef, isActive]);
useEffect(() => {
if (isActive && !isHovering) {
itemRef.current?.scrollIntoView({
behavior: 'instant',
block: 'nearest',
});
}
}, [itemRef, isActive]);
const commonProps = {
onMouseEnter,
onMouseLeave,
isActive,
};
return result.type === 'collection' ? (
<ResultCollection result={result} innerRef={itemRef} {...commonProps} />
) : (
<ResultLink result={result} innerRef={itemRef} {...commonProps} />
);
const commonProps = {
onMouseEnter,
onMouseLeave,
isActive,
};
return result.type === 'collection' ? (
<ResultCollection result={result} innerRef={itemRef} {...commonProps} />
) : (
<ResultLink result={result} innerRef={itemRef} {...commonProps} />
);
}
function ResultLink({
result,
innerRef,
...props
result,
innerRef,
...props
}: {
result: SearchResultLink;
result: SearchResultLink;
} & CommonResultProps) {
const { collections } = useCollections();
const collection = collections.find((c) => c.id === result.collection_id);
const link = collection?.links.find((l) => l.id === result.id);
const { collections } = useCollections();
const collection = collections.find((c) => c.id === result.collection_id);
const link = collection?.links.find((l) => l.id === result.id);
if (!collection || !link) return <></>;
if (!collection || !link) return <></>;
return (
<SearchItemStyle
key={result.type + result.id.toString()}
ref={innerRef}
{...props}
>
<LinkFavicon url={link.url} size={20} />
<TextEllipsis
dangerouslySetInnerHTML={{
__html: result.matched_part ?? result.name,
}}
/>
<Legend>({collection.name})</Legend>
</SearchItemStyle>
);
return (
<SearchItemStyle
key={result.type + result.id.toString()}
ref={innerRef}
{...props}
>
<LinkFavicon url={link.url} size={20} />
<TextEllipsis
dangerouslySetInnerHTML={{
__html: result.matched_part ?? result.name,
}}
/>
<Legend>({collection.name})</Legend>
</SearchItemStyle>
);
}
const ResultCollection = ({
result,
innerRef,
...props
result,
innerRef,
...props
}: {
result: SearchResultCollection;
result: SearchResultCollection;
} & CommonResultProps) => (
<SearchItemStyle
key={result.type + result.id.toString()}
ref={innerRef}
{...props}
>
<AiOutlineFolder size={24} />
<TextEllipsis
dangerouslySetInnerHTML={{
__html: result.matched_part ?? result.name,
}}
/>
</SearchItemStyle>
<SearchItemStyle
key={result.type + result.id.toString()}
ref={innerRef}
{...props}
>
<AiOutlineFolder size={24} />
<TextEllipsis
dangerouslySetInnerHTML={{
__html: result.matched_part ?? result.name,
}}
/>
</SearchItemStyle>
);

View File

@@ -4,49 +4,49 @@ import useShortcut from '~/hooks/use_shortcut';
import { SearchResult } from '~/types/search';
export default function SearchResultList({
results,
selectedItem,
setSelectedItem,
results,
selectedItem,
setSelectedItem,
}: {
results: SearchResult[];
selectedItem: SearchResult;
setSelectedItem: (result: SearchResult) => void;
results: SearchResult[];
selectedItem: SearchResult;
setSelectedItem: (result: SearchResult) => void;
}) {
const selectedItemIndex = results.findIndex(
(item) => item.id === selectedItem.id && item.type === selectedItem.type
);
const selectedItemIndex = results.findIndex(
(item) => item.id === selectedItem.id && item.type === selectedItem.type
);
useShortcut(
'ARROW_UP',
() => setSelectedItem(results[selectedItemIndex - 1]),
{
enabled: results.length > 1 && selectedItemIndex !== 0,
disableGlobalCheck: true,
}
);
useShortcut(
'ARROW_DOWN',
() => setSelectedItem(results[selectedItemIndex + 1]),
{
enabled: results.length > 1 && selectedItemIndex !== results.length - 1,
disableGlobalCheck: true,
}
);
useShortcut(
'ARROW_UP',
() => setSelectedItem(results[selectedItemIndex - 1]),
{
enabled: results.length > 1 && selectedItemIndex !== 0,
disableGlobalCheck: true,
}
);
useShortcut(
'ARROW_DOWN',
() => setSelectedItem(results[selectedItemIndex + 1]),
{
enabled: results.length > 1 && selectedItemIndex !== results.length - 1,
disableGlobalCheck: true,
}
);
return (
<UnstyledList css={{ maxHeight: '500px', overflow: 'auto' }}>
{results.map((result) => (
<SearchResultItem
result={result}
onHover={setSelectedItem}
isActive={
selectedItem &&
selectedItem.id === result.id &&
selectedItem.type === result.type
}
key={result.type + result.id.toString()}
/>
))}
</UnstyledList>
);
return (
<UnstyledList css={{ maxHeight: '500px', overflow: 'auto' }}>
{results.map((result) => (
<SearchResultItem
result={result}
onHover={setSelectedItem}
isActive={
selectedItem &&
selectedItem.id === result.id &&
selectedItem.type === result.type
}
key={result.type + result.id.toString()}
/>
))}
</UnstyledList>
);
}