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

Merge pull request #5850 from grafixeyehero/move-reusable-component

Move reusable Text Lines component to common file
This commit is contained in:
Bill Thornton 2024-08-20 16:31:17 -04:00 committed by GitHub
commit 3e8592e29e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 172 additions and 103 deletions

View file

@ -92,7 +92,7 @@ const ItemsView: FC<ItemsViewProps> = ({
listOptions.showParentTitle = true; listOptions.showParentTitle = true;
listOptions.action = 'playallfromhere'; listOptions.action = 'playallfromhere';
listOptions.smallIcon = true; listOptions.smallIcon = true;
listOptions.artist = true; listOptions.showArtist = true;
listOptions.addToListButton = true; listOptions.addToListButton = true;
} else if (viewType === LibraryTab.Albums) { } else if (viewType === LibraryTab.Albums) {
listOptions.sortBy = libraryViewSettings.SortBy; listOptions.sortBy = libraryViewSettings.SortBy;

View file

@ -0,0 +1,74 @@
import React, { type FC, type PropsWithChildren } from 'react';
import classNames from 'classnames';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import useTextLines from './useTextLines';
import type { ItemDto } from 'types/base/models/item-dto';
import type { TextLine, TextLineOpts } from './types';
interface TextWrapperProps {
isHeading?: boolean;
isLargeStyle?: boolean;
className?: string;
}
const TextWrapper: FC<PropsWithChildren<TextWrapperProps>> = ({
isHeading,
isLargeStyle,
className,
children
}) => {
if (isHeading) {
return (
<Typography className={classNames('primary', className)} variant={isLargeStyle ? 'h1' : 'h3'}>
{children}
</Typography>
);
} else {
return (
<Box className={classNames('secondary', className )}>
{children}
</Box>
);
}
};
interface TextLinesProps {
item: ItemDto;
textLineOpts?: TextLineOpts;
isLargeStyle?: boolean;
className?: string;
textClassName?: string;
}
const TextLines: FC<TextLinesProps> = ({
item,
textLineOpts,
isLargeStyle,
className,
textClassName
}) => {
const { textLines } = useTextLines({ item, textLineOpts });
const renderTextlines = (text: TextLine, index: number) => {
return (
<TextWrapper
key={index}
isHeading={index === 0}
isLargeStyle={isLargeStyle}
className={textClassName}
>
<bdi>{text.title}</bdi>
</TextWrapper>
);
};
return (
<Box className={className}>
{textLines?.map((text, index) => renderTextlines(text, index))}
</Box>
);
};
export default TextLines;

View file

@ -0,0 +1,18 @@
export interface TextLine {
title?: string;
cssClass?: string;
}
export interface TextLineOpts {
showProgramDateTime?: boolean;
showProgramTime?: boolean;
showChannel?: boolean;
showTitle?: boolean;
showParentTitle?: boolean;
showIndexNumber?: boolean;
parentTitleWithTitle?: boolean;
showArtist?: boolean;
showCurrentProgram?: boolean;
includeIndexNumber?: boolean;
includeParentInfoInTitle?: boolean;
}

View file

@ -1,27 +1,25 @@
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
import React from 'react';
import itemHelper from '../../itemHelper'; import itemHelper from '../../itemHelper';
import datetime from 'scripts/datetime'; import datetime from 'scripts/datetime';
import ListTextWrapper from './ListTextWrapper';
import type { ItemDto } from 'types/base/models/item-dto'; import type { ItemDto } from 'types/base/models/item-dto';
import type { ListOptions } from 'types/listOptions'; import type { TextLine, TextLineOpts } from './types';
import { ItemKind } from 'types/base/models/item-kind';
function getParentTitle( function getParentTitle(
showParentTitle: boolean | undefined,
item: ItemDto, item: ItemDto,
showParentTitle: boolean | undefined,
parentTitleWithTitle: boolean | undefined, parentTitleWithTitle: boolean | undefined,
displayName: string | null | undefined displayName: string | null | undefined
) { ) {
let parentTitle = null; let parentTitle;
if (showParentTitle) { if (showParentTitle) {
if (item.Type === BaseItemKind.Episode) { if (item.Type === ItemKind.Season || item.Type === ItemKind.Episode) {
parentTitle = item.SeriesName; parentTitle = item.SeriesName;
} else if (item.IsSeries || (item.EpisodeTitle && item.Name)) { } else if (item.IsSeries || (item.EpisodeTitle && item.Name)) {
parentTitle = item.Name; parentTitle = item.Name;
} }
} }
if (showParentTitle && parentTitleWithTitle) { if (showParentTitle && parentTitleWithTitle) {
if (displayName) { if (displayName && parentTitle) {
parentTitle += ' - '; parentTitle += ' - ';
} }
parentTitle = (parentTitle ?? '') + displayName; parentTitle = (parentTitle ?? '') + displayName;
@ -31,11 +29,13 @@ function getParentTitle(
function getNameOrIndexWithName( function getNameOrIndexWithName(
item: ItemDto, item: ItemDto,
listOptions: ListOptions, showIndexNumber?: boolean,
showIndexNumber: boolean | undefined includeParentInfoInTitle?: boolean,
includeIndexNumber?: boolean
) { ) {
let displayName = itemHelper.getDisplayName(item, { let displayName = itemHelper.getDisplayName(item, {
includeParentInfo: listOptions.includeParentInfoInTitle includeParentInfo: includeParentInfoInTitle,
includeIndexNumber
}); });
if (showIndexNumber && item.IndexNumber != null) { if (showIndexNumber && item.IndexNumber != null) {
@ -44,27 +44,31 @@ function getNameOrIndexWithName(
return displayName; return displayName;
} }
interface UseListTextlinesProps { interface UseTextLinesProps {
item: ItemDto; item: ItemDto;
listOptions?: ListOptions; textLineOpts?: TextLineOpts;
isLargeStyle?: boolean;
} }
function useListTextlines({ item = {}, listOptions = {}, isLargeStyle }: UseListTextlinesProps) { function useTextLines({ item, textLineOpts = {} }: UseTextLinesProps) {
const { const {
showTitle,
showProgramDateTime, showProgramDateTime,
showProgramTime, showProgramTime,
showChannel, showChannel,
showParentTitle, showParentTitle,
showIndexNumber, showIndexNumber,
parentTitleWithTitle, parentTitleWithTitle,
artist showArtist,
} = listOptions; showCurrentProgram,
const textLines: string[] = []; includeParentInfoInTitle,
includeIndexNumber
} = textLineOpts;
const addTextLine = (text: string | null) => { const textLines: TextLine[] = [];
if (text) {
textLines.push(text); const addTextLine = (textLine: TextLine) => {
if (textLine) {
textLines.push(textLine);
} }
}; };
@ -80,7 +84,7 @@ function useListTextlines({ item = {}, listOptions = {}, isLargeStyle }: UseList
minute: '2-digit' minute: '2-digit'
} }
); );
addTextLine(programDateTime); addTextLine({ title: programDateTime });
} }
}; };
@ -89,50 +93,54 @@ function useListTextlines({ item = {}, listOptions = {}, isLargeStyle }: UseList
const programTime = datetime.getDisplayTime( const programTime = datetime.getDisplayTime(
datetime.parseISO8601Date(item.StartDate) datetime.parseISO8601Date(item.StartDate)
); );
addTextLine(programTime); addTextLine({ title: programTime });
} }
}; };
const addChannelName = () => { const addChannelName = () => {
if (showChannel && item.ChannelName) { if (showChannel && item.ChannelName) {
addTextLine(item.ChannelName); addTextLine({ title: item.ChannelName });
} }
}; };
const displayName = getNameOrIndexWithName(item, listOptions, showIndexNumber); const displayName = getNameOrIndexWithName(item, showIndexNumber, includeParentInfoInTitle, includeIndexNumber);
const parentTitle = getParentTitle(showParentTitle, item, parentTitleWithTitle, displayName ); const parentTitle = getParentTitle(item, showParentTitle, parentTitleWithTitle, displayName );
const addParentTitle = () => { const addParentTitle = () => {
addTextLine(parentTitle ?? ''); if (parentTitle) {
addTextLine({ title: parentTitle });
}
}; };
const addDisplayName = () => { const addDisplayName = () => {
if (displayName && !parentTitleWithTitle) { if (displayName && !parentTitleWithTitle && showTitle !== false) {
addTextLine(displayName); addTextLine({ title: displayName });
} }
}; };
const addAlbumArtistOrArtists = () => { const addAlbumArtistOrArtists = () => {
if (item.IsFolder && artist !== false) { if (item.IsFolder && showArtist !== false) {
if (item.AlbumArtist && item.Type === BaseItemKind.MusicAlbum) { if (item.AlbumArtist && item.Type === ItemKind.MusicAlbum) {
addTextLine(item.AlbumArtist); addTextLine({ title: item.AlbumArtist });
} }
} else if (artist) { } else if (showArtist) {
const artistItems = item.ArtistItems; const artistItems = item.ArtistItems;
if (artistItems && item.Type !== BaseItemKind.MusicAlbum) { if (artistItems && item.Type !== ItemKind.MusicAlbum) {
const artists = artistItems.map((a) => a.Name).join(', '); const artists = artistItems.map((a) => a.Name).join(', ');
addTextLine(artists); addTextLine({ title: artists });
} }
} }
}; };
const addCurrentProgram = () => { const addCurrentProgram = () => {
if (item.Type === BaseItemKind.TvChannel && item.CurrentProgram) { if (item.Type === ItemKind.TvChannel && item.CurrentProgram && showCurrentProgram !== false) {
const currentProgram = itemHelper.getDisplayName( const currentProgram = itemHelper.getDisplayName(item.CurrentProgram, {
item.CurrentProgram includeParentInfo: includeParentInfoInTitle,
); includeIndexNumber
addTextLine(currentProgram); });
addTextLine({ title: currentProgram });
} }
}; };
@ -144,24 +152,9 @@ function useListTextlines({ item = {}, listOptions = {}, isLargeStyle }: UseList
addAlbumArtistOrArtists(); addAlbumArtistOrArtists();
addCurrentProgram(); addCurrentProgram();
const renderTextlines = (text: string, index: number) => {
return (
<ListTextWrapper
// eslint-disable-next-line react/no-array-index-key
key={index}
index={index}
isLargeStyle={isLargeStyle}
>
<bdi>{text}</bdi>
</ListTextWrapper>
);
};
const listTextLines = textLines?.map((text, index) => renderTextlines(text, index));
return { return {
listTextLines textLines
}; };
} }
export default useListTextlines; export default useTextLines;

View file

@ -75,7 +75,7 @@ const ListContent: FC<ListContentProps> = ({
getMissingIndicator={indicator.getMissingIndicator} getMissingIndicator={indicator.getMissingIndicator}
/> />
{listOptions.mediaInfo !== false && enableSideMediaInfo && ( {listOptions.showMediaInfo !== false && enableSideMediaInfo && (
<PrimaryMediaInfo <PrimaryMediaInfo
className='secondary listItemMediaInfo' className='secondary listItemMediaInfo'
item={item} item={item}

View file

@ -1,7 +1,8 @@
import React, { type FC } from 'react'; import React, { type FC } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import useListTextlines from './useListTextlines';
import TextLines from 'components/common/textLines/TextLines';
import PrimaryMediaInfo from '../../mediainfo/PrimaryMediaInfo'; import PrimaryMediaInfo from '../../mediainfo/PrimaryMediaInfo';
import type { ItemDto } from 'types/base/models/item-dto'; import type { ItemDto } from 'types/base/models/item-dto';
@ -30,7 +31,6 @@ const ListItemBody: FC<ListItemBodyProps> = ({
enableSideMediaInfo, enableSideMediaInfo,
getMissingIndicator getMissingIndicator
}) => { }) => {
const { listTextLines } = useListTextlines({ item, listOptions, isLargeStyle });
const cssClass = classNames( const cssClass = classNames(
'listItemBody', 'listItemBody',
{ 'itemAction': !clickEntireItem }, { 'itemAction': !clickEntireItem },
@ -40,9 +40,25 @@ const ListItemBody: FC<ListItemBodyProps> = ({
return ( return (
<Box data-action={action} className={cssClass}> <Box data-action={action} className={cssClass}>
{listTextLines} <TextLines
item={item}
textClassName='listItemBodyText'
textLineOpts={{
showProgramDateTime: listOptions.showProgramDateTime,
showProgramTime: listOptions.showProgramTime,
showChannel: listOptions.showChannel,
showParentTitle: listOptions.showParentTitle,
showIndexNumber: listOptions.showIndexNumber,
parentTitleWithTitle: listOptions.parentTitleWithTitle,
showArtist: listOptions.showArtist,
includeParentInfoInTitle: listOptions.includeParentInfoInTitle,
includeIndexNumber: listOptions.includeIndexNumber,
showCurrentProgram: listOptions.showCurrentProgram
}}
isLargeStyle={isLargeStyle}
/>
{listOptions.mediaInfo !== false && !enableSideMediaInfo && ( {listOptions.showMediaInfo !== false && !enableSideMediaInfo && (
<PrimaryMediaInfo <PrimaryMediaInfo
className='secondary listItemMediaInfo listItemBodyText' className='secondary listItemMediaInfo listItemBodyText'
item={item} item={item}

View file

@ -1,30 +0,0 @@
import React, { type FC, type PropsWithChildren } from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
interface ListTextWrapperProps {
index?: number;
isLargeStyle?: boolean;
}
const ListTextWrapper: FC<PropsWithChildren<ListTextWrapperProps>> = ({
index,
isLargeStyle,
children
}) => {
if (index === 0) {
if (isLargeStyle) {
return (
<Typography className='listItemBodyText' variant='h2'>
{children}
</Typography>
);
} else {
return <Box className='listItemBodyText'>{children}</Box>;
}
} else {
return <Box className='secondary listItemBodyText'>{children}</Box>;
}
};
export default ListTextWrapper;

View file

@ -5,6 +5,10 @@
contain: layout style; contain: layout style;
} }
.listItemMediaInfo {
align-items: center;
}
.listItem { .listItem {
background: transparent; background: transparent;
border: 0; border: 0;

View file

@ -1,7 +1,9 @@
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by'; import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
import type { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type'; import type { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
import type { ItemDto } from './base/models/item-dto'; import type { ItemDto } from './base/models/item-dto';
export interface ListOptions { import type { TextLineOpts } from 'components/common/textLines/types';
export interface ListOptions extends TextLineOpts {
items?: ItemDto[] | null; items?: ItemDto[] | null;
index?: string; index?: string;
showIndex?: boolean; showIndex?: boolean;
@ -17,21 +19,13 @@ export interface ListOptions {
highlight?: boolean; highlight?: boolean;
dragHandle?: boolean; dragHandle?: boolean;
showIndexNumberLeft?: boolean; showIndexNumberLeft?: boolean;
mediaInfo?: boolean; showMediaInfo?: boolean;
recordButton?: boolean; recordButton?: boolean;
image?: boolean; image?: boolean;
imageSource?: string; imageSource?: string;
defaultCardImageIcon?: string; defaultCardImageIcon?: string;
disableIndicators?: boolean; disableIndicators?: boolean;
imagePlayButton?: boolean; imagePlayButton?: boolean;
showProgramDateTime?: boolean;
showProgramTime?: boolean;
showChannel?: boolean;
showParentTitle?: boolean;
showIndexNumber?: boolean;
parentTitleWithTitle?: boolean;
artist?: boolean;
includeParentInfoInTitle?: boolean;
addToListButton?: boolean; addToListButton?: boolean;
infoButton?: boolean; infoButton?: boolean;
enableUserDataButtons?: boolean; enableUserDataButtons?: boolean;