mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
add list search links to command palette
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +30,11 @@
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -12,7 +12,7 @@ interface CollapsibleCommandGroupProps {
|
||||
expanded?: boolean;
|
||||
heading: string;
|
||||
onToggle?: () => void;
|
||||
subtitle?: string;
|
||||
subtitle?: ReactNode;
|
||||
}
|
||||
|
||||
export function CollapsibleCommandGroup({
|
||||
@@ -48,7 +48,7 @@ export function CollapsibleCommandGroup({
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Paper p="xs" radius="sm" withBorder>
|
||||
<Paper p="sm" radius="sm" withBorder>
|
||||
<div
|
||||
className={styles.heading}
|
||||
onClick={toggle}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
import { nanoid } from 'nanoid/non-secure';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { generatePath, useNavigate } from 'react-router';
|
||||
import { createSearchParams, generatePath, useNavigate } from 'react-router';
|
||||
|
||||
import { searchQueries } from '/@/renderer/features/search/api/search-api';
|
||||
import { CollapsibleCommandGroup } from '/@/renderer/features/search/components/collapsible-command-group';
|
||||
import { CommandItemSelectable } from '/@/renderer/features/search/components/command-item-selectable';
|
||||
import { LibraryCommandItem } from '/@/renderer/features/search/components/library-command-item';
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { Box } from '/@/shared/components/box/box';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
@@ -47,14 +51,50 @@ export function SearchAlbumArtistsSection({
|
||||
const showSection = isHome;
|
||||
const numberOfResults = hasNextPage ? `${artists.length}+` : artists.length;
|
||||
|
||||
const handleGoToPage = useCallback(() => {
|
||||
navigate(
|
||||
{
|
||||
pathname: AppRoute.LIBRARY_ALBUM_ARTISTS,
|
||||
search: createSearchParams({
|
||||
[FILTER_KEYS.SHARED.SEARCH_TERM]: debouncedQuery || query,
|
||||
}).toString(),
|
||||
},
|
||||
{ state: { navigationId: nanoid() } },
|
||||
);
|
||||
onSelectResult();
|
||||
}, [debouncedQuery, navigate, onSelectResult, query]);
|
||||
|
||||
if (!showSection) return null;
|
||||
|
||||
return (
|
||||
<CollapsibleCommandGroup
|
||||
expanded={expanded}
|
||||
heading="Artists"
|
||||
heading={t('entity.albumArtist', { count: 2, postProcess: 'titleCase' })}
|
||||
onToggle={onToggle}
|
||||
subtitle={isFetched ? t('common.numberOfResults', { numberOfResults }) : undefined}
|
||||
subtitle={
|
||||
isFetched ? (
|
||||
<>
|
||||
{query ? (
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleGoToPage();
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
size="compact-xs"
|
||||
variant="filled"
|
||||
w="8rem"
|
||||
>
|
||||
{t('common.numberOfResults', { numberOfResults })}
|
||||
</Button>
|
||||
) : null}
|
||||
</>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Box p="md">
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
import { nanoid } from 'nanoid/non-secure';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { generatePath, useNavigate } from 'react-router';
|
||||
import { createSearchParams, generatePath, useNavigate } from 'react-router';
|
||||
|
||||
import { searchQueries } from '/@/renderer/features/search/api/search-api';
|
||||
import { CollapsibleCommandGroup } from '/@/renderer/features/search/components/collapsible-command-group';
|
||||
import { CommandItemSelectable } from '/@/renderer/features/search/components/command-item-selectable';
|
||||
import { LibraryCommandItem } from '/@/renderer/features/search/components/library-command-item';
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { Box } from '/@/shared/components/box/box';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
@@ -47,14 +51,50 @@ export function SearchAlbumsSection({
|
||||
const showSection = isHome;
|
||||
const numberOfResults = hasNextPage ? `${albums.length}+` : albums.length;
|
||||
|
||||
const handleGoToPage = useCallback(() => {
|
||||
navigate(
|
||||
{
|
||||
pathname: AppRoute.LIBRARY_ALBUMS,
|
||||
search: createSearchParams({
|
||||
[FILTER_KEYS.SHARED.SEARCH_TERM]: debouncedQuery || query,
|
||||
}).toString(),
|
||||
},
|
||||
{ state: { navigationId: nanoid() } },
|
||||
);
|
||||
onSelectResult();
|
||||
}, [debouncedQuery, navigate, onSelectResult, query]);
|
||||
|
||||
if (!showSection) return null;
|
||||
|
||||
return (
|
||||
<CollapsibleCommandGroup
|
||||
expanded={expanded}
|
||||
heading="Albums"
|
||||
heading={t('entity.album', { count: 2, postProcess: 'titleCase' })}
|
||||
onToggle={onToggle}
|
||||
subtitle={isFetched ? t('common.numberOfResults', { numberOfResults }) : undefined}
|
||||
subtitle={
|
||||
isFetched ? (
|
||||
<>
|
||||
{query ? (
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleGoToPage();
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
size="compact-xs"
|
||||
variant="filled"
|
||||
w="8rem"
|
||||
>
|
||||
{t('common.numberOfResults', { numberOfResults })}
|
||||
</Button>
|
||||
) : null}
|
||||
</>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Box p="md">
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
import { nanoid } from 'nanoid/non-secure';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { generatePath, useNavigate } from 'react-router';
|
||||
import { createSearchParams, generatePath, useNavigate } from 'react-router';
|
||||
|
||||
import { searchQueries } from '/@/renderer/features/search/api/search-api';
|
||||
import { CollapsibleCommandGroup } from '/@/renderer/features/search/components/collapsible-command-group';
|
||||
import { CommandItemSelectable } from '/@/renderer/features/search/components/command-item-selectable';
|
||||
import { LibraryCommandItem } from '/@/renderer/features/search/components/library-command-item';
|
||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import { useCurrentServer } from '/@/renderer/store';
|
||||
import { Box } from '/@/shared/components/box/box';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
@@ -47,14 +51,50 @@ export function SearchSongsSection({
|
||||
const showSection = isHome;
|
||||
const numberOfResults = hasNextPage ? `${songs.length}+` : songs.length;
|
||||
|
||||
const handleGoToPage = useCallback(() => {
|
||||
navigate(
|
||||
{
|
||||
pathname: AppRoute.LIBRARY_SONGS,
|
||||
search: createSearchParams({
|
||||
[FILTER_KEYS.SHARED.SEARCH_TERM]: debouncedQuery || query,
|
||||
}).toString(),
|
||||
},
|
||||
{ state: { navigationId: nanoid() } },
|
||||
);
|
||||
onSelectResult();
|
||||
}, [debouncedQuery, navigate, onSelectResult, query]);
|
||||
|
||||
if (!showSection) return null;
|
||||
|
||||
return (
|
||||
<CollapsibleCommandGroup
|
||||
expanded={expanded}
|
||||
heading="Tracks"
|
||||
heading={t('entity.track', { count: 2, postProcess: 'titleCase' })}
|
||||
onToggle={onToggle}
|
||||
subtitle={isFetched ? t('common.numberOfResults', { numberOfResults }) : undefined}
|
||||
subtitle={
|
||||
isFetched ? (
|
||||
<>
|
||||
{query ? (
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handleGoToPage();
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
size="compact-xs"
|
||||
variant="filled"
|
||||
w="8rem"
|
||||
>
|
||||
{t('common.numberOfResults', { numberOfResults })}
|
||||
</Button>
|
||||
) : null}
|
||||
</>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Box p="md">
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
import { useLocation } from 'react-router';
|
||||
|
||||
import { SearchInput } from '/@/renderer/features/shared/components/search-input';
|
||||
import { useSearchTermFilter } from '/@/renderer/features/shared/hooks/use-search-term-filter';
|
||||
|
||||
function navigationIdFromState(state: unknown): string | undefined {
|
||||
if (state && typeof state === 'object' && 'navigationId' in state) {
|
||||
const id = (state as { navigationId: unknown }).navigationId;
|
||||
return typeof id === 'string' ? id : undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const ListSearchInput = () => {
|
||||
const { searchTerm, setSearchTerm } = useSearchTermFilter();
|
||||
const { state } = useLocation();
|
||||
const navigationId = navigationIdFromState(state);
|
||||
|
||||
return (
|
||||
<SearchInput
|
||||
defaultValue={searchTerm}
|
||||
key={navigationId ?? 'list-search-input'}
|
||||
onChange={(e) => setSearchTerm(e.target.value || null)}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user