1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Move search results to react components

This commit is contained in:
Bill Thornton 2021-06-03 10:11:49 -04:00
parent 6058a512c4
commit de54dc636a
9 changed files with 515 additions and 771 deletions

View file

@ -0,0 +1,195 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import globalize from '../../scripts/globalize';
import ServerConnections from '../ServerConnections';
import SearchResultsRow from './SearchResultsRow';
const CARD_OPTIONS = {
preferThumb: true,
inheritThumb: false,
showParentTitleOrTitle: true,
showTitle: false,
coverImage: true,
overlayMoreButton: true,
showAirTime: true,
showAirDateTime: true,
showChannelName: true
};
const LiveTVSearchResults = ({ serverId, parentId, collectionType, query }) => {
const [ apiClient, setApiClient ] = useState();
const [ movies, setMovies ] = useState([]);
const [ episodes, setEpisodes ] = useState([]);
const [ sports, setSports ] = useState([]);
const [ kids, setKids ] = useState([]);
const [ news, setNews ] = useState([]);
const [ programs, setPrograms ] = useState([]);
const [ videos, setVideos ] = useState([]);
const getDefaultParameters = () => ({
ParentId: parentId,
searchTerm: query,
Limit: 24,
Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount',
Recursive: true,
EnableTotalRecordCount: false,
ImageTypeLimit: 1,
IncludePeople: false,
IncludeMedia: false,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false
});
// FIXME: This query does not support Live TV filters
const fetchItems = (apiClient, params = {}) => apiClient?.getItems(
apiClient?.getCurrentUserId(),
{
...getDefaultParameters(),
IncludeMedia: true,
...params
}
);
const isLiveTV = () => collectionType === 'livetv';
useEffect(() => {
if (serverId) setApiClient(ServerConnections.getApiClient(serverId));
}, [ serverId ]);
useEffect(() => {
// Reset state
setMovies([]);
setEpisodes([]);
setSports([]);
setKids([]);
setNews([]);
setPrograms([]);
setVideos([]);
if (query && isLiveTV()) {
// Movies row
fetchItems(apiClient, {
IncludeItemTypes: 'LiveTvProgram',
IsMovie: true,
IsSeries: false,
IsSports: false,
IsKids: false,
IsNews: false
}).then(result => setMovies(result.Items));
// Episodes row
fetchItems(apiClient, {
IncludeItemTypes: 'LiveTvProgram',
IsMovie: false,
IsSeries: true,
IsSports: false,
IsKids: false,
IsNews: false
}).then(result => setEpisodes(result.Items));
// Sports row
fetchItems(apiClient, {
IncludeItemTypes: 'LiveTvProgram',
IsMovie: false,
IsSeries: false,
IsSports: true,
IsKids: false,
IsNews: false
}).then(result => setSports(result.Items));
// Kids row
fetchItems(apiClient, {
IncludeItemTypes: 'LiveTvProgram',
IsMovie: false,
IsSeries: false,
IsSports: false,
IsKids: true,
IsNews: false
}).then(result => setKids(result.Items));
// News row
fetchItems(apiClient, {
IncludeItemTypes: 'LiveTvProgram',
IsMovie: false,
IsSeries: false,
IsSports: false,
IsKids: false,
IsNews: true
}).then(result => setNews(result.Items));
// Programs row
fetchItems(apiClient, {
IncludeItemTypes: 'LiveTvProgram',
IsMovie: false,
IsSeries: false,
IsSports: false,
IsKids: false,
IsNews: false
}).then(result => setPrograms(result.Items));
// NOTE: I believe this is supposed to be home videos, but it
// includes TV channels so it should probably be included for Live TV
// Videos row
fetchItems(apiClient, {
MediaTypes: 'Video',
ExcludeItemTypes: 'Movie,Episode'
}).then(result => setVideos(result.Items));
}
}, [ query ]);
return (
<div
className={classNames(
'searchResults',
'padded-bottom-page',
'padded-top',
{ 'hide': !query || !isLiveTV() }
)}
>
<SearchResultsRow
title={globalize.translate('Movies')}
items={movies}
cardOptions={{
...CARD_OPTIONS,
shape: 'overflowPortrait'
}}
/>
<SearchResultsRow
title={globalize.translate('Episodes')}
items={episodes}
cardOptions={CARD_OPTIONS}
/>
<SearchResultsRow
title={globalize.translate('Sports')}
items={sports}
cardOptions={CARD_OPTIONS}
/>
<SearchResultsRow
title={globalize.translate('Kids')}
items={kids}
cardOptions={CARD_OPTIONS}
/>
<SearchResultsRow
title={globalize.translate('News')}
items={news}
cardOptions={CARD_OPTIONS}
/>
<SearchResultsRow
title={globalize.translate('Programs')}
items={programs}
cardOptions={CARD_OPTIONS}
/>
<SearchResultsRow
title={globalize.translate('Videos')}
items={videos}
cardOptions={{ showParentTitle: true }}
/>
</div>
);
};
LiveTVSearchResults.propTypes = {
serverId: PropTypes.string,
parentId: PropTypes.string,
collectionType: PropTypes.string,
query: PropTypes.string
};
export default LiveTVSearchResults;

View file

@ -0,0 +1,258 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import globalize from '../../scripts/globalize';
import ServerConnections from '../ServerConnections';
import SearchResultsRow from './SearchResultsRow';
const SearchResultsComponent = ({ serverId, parentId, collectionType, query }) => {
const [ apiClient, setApiClient ] = useState();
const [ movies, setMovies ] = useState([]);
const [ shows, setShows ] = useState([]);
const [ episodes, setEpisodes ] = useState([]);
const [ programs, setPrograms ] = useState([]);
const [ videos, setVideos ] = useState([]);
const [ playlists, setPlaylists ] = useState([]);
const [ artists, setArtists ] = useState([]);
const [ albums, setAlbums ] = useState([]);
const [ songs, setSongs ] = useState([]);
const [ photoAlbums, setPhotoAlbums ] = useState([]);
const [ photos, setPhotos ] = useState([]);
const [ audioBooks, setAudioBooks ] = useState([]);
const [ books, setBooks ] = useState([]);
const [ people, setPeople ] = useState([]);
const getDefaultParameters = () => ({
ParentId: parentId,
searchTerm: query,
Limit: 24,
Fields: 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount',
Recursive: true,
EnableTotalRecordCount: false,
ImageTypeLimit: 1,
IncludePeople: false,
IncludeMedia: false,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false
});
const fetchArtists = (apiClient, params = {}) => apiClient?.getArtists(
apiClient?.getCurrentUserId(),
{
...getDefaultParameters(),
IncludeArtists: true,
...params
}
);
const fetchItems = (apiClient, params = {}) => apiClient?.getItems(
apiClient?.getCurrentUserId(),
{
...getDefaultParameters(),
IncludeMedia: true,
...params
}
);
const fetchPeople = (apiClient, params = {}) => apiClient?.getPeople(
apiClient?.getCurrentUserId(),
{
...getDefaultParameters(),
IncludePeople: true,
...params
}
);
const isMovies = () => collectionType === 'movies';
const isMusic = () => collectionType === 'music';
const isTVShows = () => collectionType === 'tvshows' || collectionType === 'tv';
useEffect(() => {
if (serverId) setApiClient(ServerConnections.getApiClient(serverId));
}, [ serverId ]);
useEffect(() => {
// Reset state
setMovies([]);
setShows([]);
setEpisodes([]);
setPrograms([]);
setVideos([]);
setPlaylists([]);
setArtists([]);
setAlbums([]);
setSongs([]);
setPhotoAlbums([]);
setPhotos([]);
setAudioBooks([]);
setBooks([]);
setPeople([]);
if (query) {
// Movie libraries
if (!collectionType || isMovies()) {
// Movies row
fetchItems(apiClient, { IncludeItemTypes: 'Movie' })
.then(result => setMovies(result.Items));
}
// TV Show libraries
if (!collectionType || isTVShows()) {
// Shows row
fetchItems(apiClient, { IncludeItemTypes: 'Series' })
.then(result => setShows(result.Items));
// Episodes row
fetchItems(apiClient, { IncludeItemTypes: 'Episode' })
.then(result => setEpisodes(result.Items));
}
// People are included for Movies and TV Shows
if (!collectionType || isMovies() || isTVShows()) {
// People row
fetchPeople(apiClient).then(result => setPeople(result.Items));
}
// Music libraries
if (!collectionType || isMusic()) {
// Playlists row
fetchItems(apiClient, { IncludeItemTypes: 'Playlist' })
.then(results => setPlaylists(results.Items));
// Artists row
fetchArtists(apiClient).then(result => setArtists(result.Items));
// Albums row
fetchItems(apiClient, { IncludeItemTypes: 'MusicAlbum' })
.then(result => setAlbums(result.Items));
// Songs row
fetchItems(apiClient, { IncludeItemTypes: 'Audio' })
.then(result => setSongs(result.Items));
}
// Other libraries do not support in-library search currently
if (!collectionType) {
// Programs row
fetchItems(apiClient, { IncludeItemTypes: 'LiveTvProgram' })
.then(result => setPrograms(result.Items));
// Videos row
fetchItems(apiClient, {
MediaTypes: 'Video',
ExcludeItemTypes: 'Movie,Episode'
}).then(result => setVideos(result.Items));
// Photo Albums row
fetchItems(apiClient, { IncludeItemTypes: 'PhotoAlbum' })
.then(results => setPhotoAlbums(results.Items));
// Photos row
fetchItems(apiClient, { IncludeItemTypes: 'Photo' })
.then(results => setPhotos(results.Items));
// Audio Books row
fetchItems(apiClient, { IncludeItemTypes: 'AudioBook' })
.then(results => setAudioBooks(results.Items));
// Books row
fetchItems(apiClient, { IncludeItemTypes: 'Book' })
.then(results => setBooks(results.Items));
}
}
}, [ query ]);
return (
<div
className={classNames(
'searchResults',
'padded-bottom-page',
'padded-top',
{ 'hide': !query || collectionType === 'livetv' }
)}
>
<SearchResultsRow
title={globalize.translate('Movies')}
items={movies}
cardOptions={{ showYear: true }}
/>
<SearchResultsRow
title={globalize.translate('Shows')}
items={shows}
cardOptions={{ showYear: true }}
/>
<SearchResultsRow
title={globalize.translate('Episodes')}
items={episodes}
cardOptions={{
coverImage: true,
showParentTitle: true
}}
/>
<SearchResultsRow
title={globalize.translate('Programs')}
items={programs}
cardOptions={{
preferThumb: true,
inheritThumb: false,
showParentTitleOrTitle: true,
showTitle: false,
coverImage: true,
overlayMoreButton: true,
showAirTime: true,
showAirDateTime: true,
showChannelName: true
}}
/>
<SearchResultsRow
title={globalize.translate('Videos')}
items={videos}
cardOptions={{ showParentTitle: true }}
/>
<SearchResultsRow
title={globalize.translate('Playlists')}
items={playlists}
/>
<SearchResultsRow
title={globalize.translate('Artists')}
items={artists}
cardOptions={{ coverImage: true }}
/>
<SearchResultsRow
title={globalize.translate('Albums')}
items={albums}
cardOptions={{ showParentTitle: true }}
/>
<SearchResultsRow
title={globalize.translate('Songs')}
items={songs}
cardOptions={{ showParentTitle: true }}
/>
<SearchResultsRow
title={globalize.translate('HeaderPhotoAlbums')}
items={photoAlbums}
/>
<SearchResultsRow
title={globalize.translate('Photos')}
items={photos}
/>
<SearchResultsRow
title={globalize.translate('HeaderAudioBooks')}
items={audioBooks}
/>
<SearchResultsRow
title={globalize.translate('Books')}
items={books}
/>
<SearchResultsRow
title={globalize.translate('People')}
items={people}
cardOptions={{ coverImage: true }}
/>
</div>
);
};
SearchResultsComponent.propTypes = {
serverId: PropTypes.string,
parentId: PropTypes.string,
collectionType: PropTypes.string,
query: PropTypes.string
};
export default SearchResultsComponent;

View file

@ -1,44 +0,0 @@
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from 'react';
import SearchResults from './searchresults';
const SearchResultsComponent = ({ serverId, parentId, collectionType, query }) => {
const [ searchResults, setSearchResults ] = useState(null);
const searchResultsElement = useRef(null);
useEffect(() => {
setSearchResults(
new SearchResults({
element: searchResultsElement.current,
serverId: serverId || ApiClient.serverId(),
parentId,
collectionType
})
);
return () => {
searchResults?.destroy();
};
}, []);
useEffect(() => {
searchResults?.search(query);
}, [ query ]);
return (
<div
className='searchResults padded-bottom-page padded-top'
ref={searchResultsElement}
/>
);
};
SearchResultsComponent.propTypes = {
serverId: PropTypes.string,
parentId: PropTypes.string,
collectionType: PropTypes.string,
query: PropTypes.string
};
export default SearchResultsComponent;

View file

@ -0,0 +1,48 @@
import PropTypes from 'prop-types';
import React, { useEffect, useRef } from 'react';
import cardBuilder from '../cardbuilder/cardBuilder';
// There seems to be some compatibility issues here between
// React and our legacy web components, so we need to inject
// them as an html string for now =/
const createScroller = ({ title = '' }) => ({
__html: `<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${title}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>`
});
const SearchResultsRow = ({ title, items = [], cardOptions = {} }) => {
const element = useRef(null);
useEffect(() => {
cardBuilder.buildCards(items, {
itemsContainer: element.current?.querySelector('.itemsContainer'),
parentContainer: element.current,
shape: 'autooverflow',
scalable: true,
showTitle: true,
overlayText: false,
centerText: true,
allowBottomPadding: false,
...cardOptions
});
}, [ items ]);
return (
<div
ref={element}
className='verticalSection'
dangerouslySetInnerHTML={createScroller({ title })}
/>
);
};
SearchResultsRow.propTypes = {
title: PropTypes.string,
items: PropTypes.array,
cardOptions: PropTypes.object
};
export default SearchResultsRow;

View file

@ -1,581 +0,0 @@
import layoutManager from '../layoutManager';
import globalize from '../../scripts/globalize';
import cardBuilder from '../cardbuilder/cardBuilder';
import '../../elements/emby-scroller/emby-scroller';
import '../../elements/emby-itemscontainer/emby-itemscontainer';
import '../../elements/emby-button/emby-button';
import ServerConnections from '../ServerConnections';
import template from './searchresults.template.html';
function getSearchHints(instance, apiClient, query) {
if (!query.searchTerm) {
return Promise.resolve({
SearchHints: []
});
}
let allowSearch = true;
const queryIncludeItemTypes = query.IncludeItemTypes;
if (instance.options.collectionType === 'tvshows') {
if (query.IncludeArtists) {
allowSearch = false;
} else if (queryIncludeItemTypes === 'Movie' ||
queryIncludeItemTypes === 'LiveTvProgram' ||
queryIncludeItemTypes === 'MusicAlbum' ||
queryIncludeItemTypes === 'Audio' ||
queryIncludeItemTypes === 'Book' ||
queryIncludeItemTypes === 'AudioBook' ||
queryIncludeItemTypes === 'Playlist' ||
queryIncludeItemTypes === 'PhotoAlbum' ||
query.MediaTypes === 'Video' ||
query.MediaTypes === 'Photo') {
allowSearch = false;
}
} else if (instance.options.collectionType === 'movies') {
if (query.IncludeArtists) {
allowSearch = false;
} else if (queryIncludeItemTypes === 'Series' ||
queryIncludeItemTypes === 'Episode' ||
queryIncludeItemTypes === 'LiveTvProgram' ||
queryIncludeItemTypes === 'MusicAlbum' ||
queryIncludeItemTypes === 'Audio' ||
queryIncludeItemTypes === 'Book' ||
queryIncludeItemTypes === 'AudioBook' ||
queryIncludeItemTypes === 'Playlist' ||
queryIncludeItemTypes === 'PhotoAlbum' ||
query.MediaTypes === 'Video' ||
query.MediaTypes === 'Photo') {
allowSearch = false;
}
} else if (instance.options.collectionType === 'music') {
if (query.People) {
allowSearch = false;
} else if (queryIncludeItemTypes === 'Series' ||
queryIncludeItemTypes === 'Episode' ||
queryIncludeItemTypes === 'LiveTvProgram' ||
queryIncludeItemTypes === 'Movie') {
allowSearch = false;
}
} else if (instance.options.collectionType === 'livetv') {
if (query.IncludeArtists || query.IncludePeople) {
allowSearch = false;
} else if (queryIncludeItemTypes === 'Series' ||
queryIncludeItemTypes === 'Episode' ||
queryIncludeItemTypes === 'MusicAlbum' ||
queryIncludeItemTypes === 'Audio' ||
queryIncludeItemTypes === 'Book' ||
queryIncludeItemTypes === 'AudioBook' ||
queryIncludeItemTypes === 'PhotoAlbum' ||
queryIncludeItemTypes === 'Movie' ||
query.MediaTypes === 'Video' ||
query.MediaTypes === 'Photo') {
allowSearch = false;
}
}
if (queryIncludeItemTypes === 'NullType') {
allowSearch = false;
}
if (!allowSearch) {
return Promise.resolve({
SearchHints: []
});
}
// Convert the search hint query to a regular item query
if (apiClient.isMinServerVersion('3.4.1.31')) {
query.Fields = 'PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount';
query.Recursive = true;
query.EnableTotalRecordCount = false;
query.ImageTypeLimit = 1;
let methodName = 'getItems';
if (!query.IncludeMedia) {
if (query.IncludePeople) {
methodName = 'getPeople';
} else if (query.IncludeArtists) {
methodName = 'getArtists';
}
}
return apiClient[methodName](apiClient.getCurrentUserId(), query);
}
query.UserId = apiClient.getCurrentUserId();
return apiClient.getSearchHints(query);
}
function search(instance, apiClient, context, value) {
if (value || layoutManager.tv) {
instance.mode = 'search';
} else {
instance.mode = 'suggestions';
}
if (instance.options.collectionType === 'livetv') {
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'LiveTvProgram',
IsMovie: true,
IsKids: false,
IsNews: false
}, context, '.movieResults', {
preferThumb: true,
inheritThumb: false,
shape: (enableScrollX() ? 'overflowPortrait' : 'portrait'),
showParentTitleOrTitle: true,
showTitle: false,
centerText: true,
coverImage: true,
overlayText: false,
overlayMoreButton: true,
showAirTime: true,
showAirDateTime: true,
showChannelName: true
});
} else {
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'Movie'
}, context, '.movieResults', {
showTitle: true,
overlayText: false,
centerText: true,
showYear: true
});
}
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'Series'
}, context, '.seriesResults', {
showTitle: true,
overlayText: false,
centerText: true,
showYear: true
});
if (instance.options.collectionType === 'livetv') {
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'LiveTvProgram',
IsSeries: true,
IsSports: false,
IsKids: false,
IsNews: false
}, context, '.episodeResults', {
preferThumb: true,
inheritThumb: false,
shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'),
showParentTitleOrTitle: true,
showTitle: false,
centerText: true,
coverImage: true,
overlayText: false,
overlayMoreButton: true,
showAirTime: true,
showAirDateTime: true,
showChannelName: true
});
} else {
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'Episode'
}, context, '.episodeResults', {
coverImage: true,
showTitle: true,
showParentTitle: true
});
}
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
// NullType to hide
IncludeItemTypes: instance.options.collectionType === 'livetv' ? 'LiveTvProgram' : 'NullType',
IsSports: true
}, context, '.sportsResults', {
preferThumb: true,
inheritThumb: false,
shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'),
showParentTitleOrTitle: true,
showTitle: false,
centerText: true,
coverImage: true,
overlayText: false,
overlayMoreButton: true,
showAirTime: true,
showAirDateTime: true,
showChannelName: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
// NullType to hide
IncludeItemTypes: instance.options.collectionType === 'livetv' ? 'LiveTvProgram' : 'NullType',
IsKids: true
}, context, '.kidsResults', {
preferThumb: true,
inheritThumb: false,
shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'),
showParentTitleOrTitle: true,
showTitle: false,
centerText: true,
coverImage: true,
overlayText: false,
overlayMoreButton: true,
showAirTime: true,
showAirDateTime: true,
showChannelName: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
// NullType to hide
IncludeItemTypes: instance.options.collectionType === 'livetv' ? 'LiveTvProgram' : 'NullType',
IsNews: true
}, context, '.newsResults', {
preferThumb: true,
inheritThumb: false,
shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'),
showParentTitleOrTitle: true,
showTitle: false,
centerText: true,
coverImage: true,
overlayText: false,
overlayMoreButton: true,
showAirTime: true,
showAirDateTime: true,
showChannelName: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'LiveTvProgram',
IsMovie: instance.options.collectionType === 'livetv' ? false : null,
IsSeries: instance.options.collectionType === 'livetv' ? false : null,
IsSports: instance.options.collectionType === 'livetv' ? false : null,
IsKids: instance.options.collectionType === 'livetv' ? false : null,
IsNews: instance.options.collectionType === 'livetv' ? false : null
}, context, '.programResults', {
preferThumb: true,
inheritThumb: false,
shape: (enableScrollX() ? 'overflowBackdrop' : 'backdrop'),
showParentTitleOrTitle: true,
showTitle: false,
centerText: true,
coverImage: true,
overlayText: false,
overlayMoreButton: true,
showAirTime: true,
showAirDateTime: true,
showChannelName: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
MediaTypes: 'Video',
ExcludeItemTypes: 'Movie,Episode'
}, context, '.videoResults', {
showParentTitle: true,
showTitle: true,
overlayText: false,
centerText: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: true,
IncludeMedia: false,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false
}, context, '.peopleResults', {
coverImage: true,
showTitle: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: false,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: true
}, context, '.artistResults', {
coverImage: true,
showTitle: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'MusicAlbum'
}, context, '.albumResults', {
showParentTitle: true,
showTitle: true,
overlayText: false,
centerText: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'Audio'
}, context, '.songResults', {
showParentTitle: true,
showTitle: true,
overlayText: false,
centerText: true,
overlayPlayButton: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
MediaTypes: 'Photo'
}, context, '.photoResults', {
showParentTitle: false,
showTitle: true,
overlayText: false,
centerText: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'PhotoAlbum'
}, context, '.photoAlbumResults', {
showTitle: true,
overlayText: false,
centerText: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'Book'
}, context, '.bookResults', {
showTitle: true,
overlayText: false,
centerText: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'AudioBook'
}, context, '.audioBookResults', {
showTitle: true,
overlayText: false,
centerText: true
});
searchType(instance, apiClient, {
searchTerm: value,
IncludePeople: false,
IncludeMedia: true,
IncludeGenres: false,
IncludeStudios: false,
IncludeArtists: false,
IncludeItemTypes: 'Playlist'
}, context, '.playlistResults', {
showTitle: true,
overlayText: false,
centerText: true
});
}
function searchType(instance, apiClient, query, context, section, cardOptions) {
query.Limit = enableScrollX() ? 24 : 16;
query.ParentId = instance.options.parentId;
getSearchHints(instance, apiClient, query).then(function (result) {
populateResults(result, context, section, cardOptions);
});
}
function populateResults(result, context, section, cardOptions) {
section = context.querySelector(section);
const items = result.Items || result.SearchHints;
const itemsContainer = section.querySelector('.itemsContainer');
cardBuilder.buildCards(items, Object.assign({
itemsContainer: itemsContainer,
parentContainer: section,
shape: enableScrollX() ? 'autooverflow' : 'auto',
scalable: true,
overlayText: false,
centerText: true,
allowBottomPadding: !enableScrollX()
}, cardOptions || {}));
}
function enableScrollX() {
return true;
}
function replaceAll(originalString, strReplace, strWith) {
const reg = new RegExp(strReplace, 'ig');
return originalString.replace(reg, strWith);
}
function embed(elem, instance) {
let workingTemplate = template;
if (!enableScrollX()) {
workingTemplate = replaceAll(workingTemplate, 'data-horizontal="true"', 'data-horizontal="false"');
workingTemplate = replaceAll(workingTemplate, 'itemsContainer scrollSlider', 'itemsContainer scrollSlider vertical-wrap');
}
const html = globalize.translateHtml(workingTemplate, 'core');
elem.innerHTML = html;
elem.classList.add('searchResults');
instance.search('');
}
class SearchResults {
constructor(options) {
this.options = options;
embed(options.element, this);
}
search(value) {
const apiClient = ServerConnections.getApiClient(this.options.serverId);
search(this, apiClient, this.options.element, value);
}
destroy() {
const options = this.options;
if (options) {
options.element.classList.remove('searchFields');
}
this.options = null;
}
}
export default SearchResults;

View file

@ -1,145 +0,0 @@
<div class="hide verticalSection searchSuggestions" style="text-align:center;">
<div>
<h2 class="sectionTitle padded-left padded-right">${Suggestions}</h2>
</div>
<div class="searchSuggestionsList padded-left padded-right">
</div>
</div>
<div class="hide verticalSection movieResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Movies}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection seriesResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Shows}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection episodeResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Episodes}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection sportsResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Sports}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection kidsResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Kids}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection newsResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${News}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection programResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Programs}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection videoResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Videos}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection playlistResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Playlists}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection artistResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Artists}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection albumResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Albums}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection songResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Songs}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection photoAlbumResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${HeaderPhotoAlbums}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection photoResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Photos}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection audioBookResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${HeaderAudioBooks}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection bookResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${Books}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>
<div class="hide verticalSection peopleResults">
<h2 class="sectionTitle sectionTitle-cards focuscontainer-x padded-left padded-right">${People}</h2>
<div is="emby-scroller" data-horizontal="true" data-centerfocus="card" class="padded-top-focusscale padded-bottom-focusscale">
<div is="emby-itemscontainer" class="focuscontainer-x itemsContainer scrollSlider"></div>
</div>
</div>