mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2025-02-23 16:01:44 +00:00
Add reusable component
This commit is contained in:
parent
c3b5d50313
commit
cc87ba3859
74
package-lock.json
generated
74
package-lock.json
generated
@ -25,6 +25,7 @@
|
||||
"@react-hook/resize-observer": "1.2.6",
|
||||
"@tanstack/react-query": "4.36.1",
|
||||
"@tanstack/react-query-devtools": "4.36.1",
|
||||
"@types/react-lazy-load-image-component": "1.6.3",
|
||||
"abortcontroller-polyfill": "1.7.5",
|
||||
"blurhash": "2.0.5",
|
||||
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
|
||||
@ -52,7 +53,9 @@
|
||||
"native-promise-only": "0.8.1",
|
||||
"pdfjs-dist": "3.11.174",
|
||||
"react": "17.0.2",
|
||||
"react-blurhash": "0.3.0",
|
||||
"react-dom": "17.0.2",
|
||||
"react-lazy-load-image-component": "1.6.0",
|
||||
"react-router-dom": "6.21.3",
|
||||
"resize-observer-polyfill": "1.5.1",
|
||||
"screenfull": "6.0.2",
|
||||
@ -4705,6 +4708,15 @@
|
||||
"@types/react": "^17"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-lazy-load-image-component": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.3.tgz",
|
||||
"integrity": "sha512-HsIsYz7yWWTh/bftdzGnijKD26JyofLRqM/RM80sxs7Gk13G83ew8R/ra2XzXuiZfjNEjAq/Va+NBHFF9ciwxA==",
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
|
||||
@ -12671,8 +12683,7 @@
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"dev": true
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
@ -12686,6 +12697,11 @@
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
|
||||
},
|
||||
"node_modules/lodash.truncate": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
||||
@ -16202,6 +16218,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-blurhash": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-blurhash/-/react-blurhash-0.3.0.tgz",
|
||||
"integrity": "sha512-XlKr4Ns1iYFRnk6DkAblNbAwN/bTJvxTVoxMvmTcURdc5oLoXZwqAF9N3LZUh/HT+QFlq5n6IS6VsDGsviYAiQ==",
|
||||
"peerDependencies": {
|
||||
"blurhash": "^2.0.3",
|
||||
"react": ">=15"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
@ -16220,6 +16245,19 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/react-lazy-load-image-component": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.0.tgz",
|
||||
"integrity": "sha512-8KFkDTgjh+0+PVbH+cx0AgxLGbdTsxWMnxXzU5HEUztqewk9ufQAu8cstjZhyvtMIPsdMcPZfA0WAa7HtjQbBQ==",
|
||||
"dependencies": {
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x",
|
||||
"react-dom": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.21.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.3.tgz",
|
||||
@ -25943,6 +25981,15 @@
|
||||
"@types/react": "^17"
|
||||
}
|
||||
},
|
||||
"@types/react-lazy-load-image-component": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.3.tgz",
|
||||
"integrity": "sha512-HsIsYz7yWWTh/bftdzGnijKD26JyofLRqM/RM80sxs7Gk13G83ew8R/ra2XzXuiZfjNEjAq/Va+NBHFF9ciwxA==",
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "4.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
|
||||
@ -31866,8 +31913,7 @@
|
||||
"lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"dev": true
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
@ -31881,6 +31927,11 @@
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
|
||||
},
|
||||
"lodash.truncate": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
||||
@ -34295,6 +34346,12 @@
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"react-blurhash": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-blurhash/-/react-blurhash-0.3.0.tgz",
|
||||
"integrity": "sha512-XlKr4Ns1iYFRnk6DkAblNbAwN/bTJvxTVoxMvmTcURdc5oLoXZwqAF9N3LZUh/HT+QFlq5n6IS6VsDGsviYAiQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
@ -34310,6 +34367,15 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"react-lazy-load-image-component": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.0.tgz",
|
||||
"integrity": "sha512-8KFkDTgjh+0+PVbH+cx0AgxLGbdTsxWMnxXzU5HEUztqewk9ufQAu8cstjZhyvtMIPsdMcPZfA0WAa7HtjQbBQ==",
|
||||
"requires": {
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "6.21.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.3.tgz",
|
||||
|
@ -86,6 +86,7 @@
|
||||
"@react-hook/resize-observer": "1.2.6",
|
||||
"@tanstack/react-query": "4.36.1",
|
||||
"@tanstack/react-query-devtools": "4.36.1",
|
||||
"@types/react-lazy-load-image-component": "1.6.3",
|
||||
"abortcontroller-polyfill": "1.7.5",
|
||||
"blurhash": "2.0.5",
|
||||
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
|
||||
@ -113,7 +114,9 @@
|
||||
"native-promise-only": "0.8.1",
|
||||
"pdfjs-dist": "3.11.174",
|
||||
"react": "17.0.2",
|
||||
"react-blurhash": "0.3.0",
|
||||
"react-dom": "17.0.2",
|
||||
"react-lazy-load-image-component": "1.6.0",
|
||||
"react-router-dom": "6.21.3",
|
||||
"resize-observer-polyfill": "1.5.1",
|
||||
"screenfull": "6.0.2",
|
||||
|
56
src/components/common/DefaultIconText.tsx
Normal file
56
src/components/common/DefaultIconText.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC } from 'react';
|
||||
import Icon from '@mui/material/Icon';
|
||||
import imageHelper from 'utils/image';
|
||||
import DefaultName from './DefaultName';
|
||||
import type { ItemDto } from 'types/itemDto';
|
||||
|
||||
interface DefaultIconTextProps {
|
||||
item: ItemDto;
|
||||
defaultCardImageIcon?: string;
|
||||
}
|
||||
|
||||
const DefaultIconText: FC<DefaultIconTextProps> = ({
|
||||
item,
|
||||
defaultCardImageIcon
|
||||
}) => {
|
||||
if (item.CollectionType) {
|
||||
return (
|
||||
<Icon
|
||||
className='cardImageIcon'
|
||||
sx={{ color: 'inherit', fontSize: '5em' }}
|
||||
aria-hidden='true'
|
||||
>
|
||||
{imageHelper.getLibraryIcon(item.CollectionType)}
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
if (item.Type && !(item.Type === BaseItemKind.TvChannel || item.Type === BaseItemKind.Studio )) {
|
||||
return (
|
||||
<Icon
|
||||
className='cardImageIcon'
|
||||
sx={{ color: 'inherit', fontSize: '5em' }}
|
||||
aria-hidden='true'
|
||||
>
|
||||
{imageHelper.getItemTypeIcon(item.Type)}
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
if (defaultCardImageIcon) {
|
||||
return (
|
||||
<Icon
|
||||
className='cardImageIcon'
|
||||
sx={{ color: 'inherit', fontSize: '5em' }}
|
||||
aria-hidden='true'
|
||||
>
|
||||
{defaultCardImageIcon}
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
return <DefaultName item={item} />;
|
||||
};
|
||||
|
||||
export default DefaultIconText;
|
23
src/components/common/DefaultName.tsx
Normal file
23
src/components/common/DefaultName.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React, { FC } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import escapeHTML from 'escape-html';
|
||||
import itemHelper from 'components/itemHelper';
|
||||
import { isUsingLiveTvNaming } from '../cardbuilder/cardBuilderUtils';
|
||||
import type { ItemDto } from 'types/itemDto';
|
||||
|
||||
interface DefaultNameProps {
|
||||
item: ItemDto;
|
||||
}
|
||||
|
||||
const DefaultName: FC<DefaultNameProps> = ({ item }) => {
|
||||
const defaultName = isUsingLiveTvNaming(item.Type) ?
|
||||
item.Name :
|
||||
itemHelper.getDisplayName(item);
|
||||
return (
|
||||
<Box className='cardText cardDefaultText'>
|
||||
{escapeHTML(defaultName)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DefaultName;
|
67
src/components/common/Image.tsx
Normal file
67
src/components/common/Image.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import React, { FC, useCallback, useState } from 'react';
|
||||
import { BlurhashCanvas } from 'react-blurhash';
|
||||
import { LazyLoadImage } from 'react-lazy-load-image-component';
|
||||
|
||||
const imageStyle: React.CSSProperties = {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
zIndex: 0
|
||||
};
|
||||
|
||||
interface ImageProps {
|
||||
imgUrl: string;
|
||||
blurhash?: string;
|
||||
containImage: boolean;
|
||||
}
|
||||
|
||||
const Image: FC<ImageProps> = ({
|
||||
imgUrl,
|
||||
blurhash,
|
||||
containImage
|
||||
}) => {
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
const [isLoadStarted, setIsLoadStarted] = useState(false);
|
||||
const handleLoad = useCallback(() => {
|
||||
setIsLoaded(true);
|
||||
}, []);
|
||||
|
||||
const handleLoadStarted = useCallback(() => {
|
||||
setIsLoadStarted(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!isLoaded && isLoadStarted && blurhash && (
|
||||
<BlurhashCanvas
|
||||
hash={blurhash}
|
||||
width= {20}
|
||||
height={20}
|
||||
punch={1}
|
||||
style={{
|
||||
...imageStyle,
|
||||
borderRadius: '0.2em',
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<LazyLoadImage
|
||||
key={imgUrl}
|
||||
src={imgUrl}
|
||||
style={{
|
||||
...imageStyle,
|
||||
objectFit: containImage ? 'contain' : 'cover'
|
||||
}}
|
||||
onLoad={handleLoad}
|
||||
beforeLoad={handleLoadStarted}
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Image;
|
22
src/components/common/InfoIconButton.tsx
Normal file
22
src/components/common/InfoIconButton.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React, { FC } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import InfoIcon from '@mui/icons-material/Info';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
interface InfoIconButtonProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const InfoIconButton: FC<InfoIconButtonProps> = ({ className }) => {
|
||||
return (
|
||||
<IconButton
|
||||
className={className}
|
||||
data-action='link'
|
||||
title={globalize.translate('ButtonInfo')}
|
||||
>
|
||||
<InfoIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoIconButton;
|
36
src/components/common/Media.tsx
Normal file
36
src/components/common/Media.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { BaseItemKind, ImageType } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC } from 'react';
|
||||
import Image from './Image';
|
||||
import DefaultIconText from './DefaultIconText';
|
||||
import type { ItemDto } from 'types/itemDto';
|
||||
|
||||
interface MediaProps {
|
||||
item: ItemDto;
|
||||
imgUrl: string | undefined;
|
||||
blurhash: string | undefined;
|
||||
imageType?: ImageType
|
||||
defaultCardImageIcon?: string
|
||||
}
|
||||
|
||||
const Media: FC<MediaProps> = ({
|
||||
item,
|
||||
imgUrl,
|
||||
blurhash,
|
||||
imageType,
|
||||
defaultCardImageIcon
|
||||
}) => {
|
||||
return imgUrl ? (
|
||||
<Image
|
||||
imgUrl={imgUrl}
|
||||
blurhash={blurhash}
|
||||
containImage={item.Type === BaseItemKind.TvChannel || imageType === ImageType.Logo}
|
||||
/>
|
||||
) : (
|
||||
<DefaultIconText
|
||||
item={item}
|
||||
defaultCardImageIcon={defaultCardImageIcon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Media;
|
23
src/components/common/MoreVertIconButton.tsx
Normal file
23
src/components/common/MoreVertIconButton.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React, { FC } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
interface MoreVertIconButtonProps {
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
}
|
||||
|
||||
const MoreVertIconButton: FC<MoreVertIconButtonProps> = ({ className, iconClassName }) => {
|
||||
return (
|
||||
<IconButton
|
||||
className={className}
|
||||
data-action='menu'
|
||||
title={globalize.translate('ButtonMore')}
|
||||
>
|
||||
<MoreVertIcon className={iconClassName} />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreVertIconButton;
|
25
src/components/common/NoItemsMessage.tsx
Normal file
25
src/components/common/NoItemsMessage.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React, { FC } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
interface NoItemsMessageProps {
|
||||
noItemsMessage?: string;
|
||||
}
|
||||
|
||||
const NoItemsMessage: FC<NoItemsMessageProps> = ({
|
||||
noItemsMessage = 'MessageNoItemsAvailable'
|
||||
}) => {
|
||||
return (
|
||||
<Box className='noItemsMessage centerMessage'>
|
||||
<Typography variant='h2'>
|
||||
{globalize.translate('MessageNothingHere')}
|
||||
</Typography>
|
||||
<Typography paragraph variant='h2'>
|
||||
{globalize.translate(noItemsMessage)}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoItemsMessage;
|
25
src/components/common/PlayArrowIconButton.tsx
Normal file
25
src/components/common/PlayArrowIconButton.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React, { FC } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
interface PlayArrowIconButtonProps {
|
||||
className: string;
|
||||
action: string;
|
||||
title: string;
|
||||
iconClassName?: string;
|
||||
}
|
||||
|
||||
const PlayArrowIconButton: FC<PlayArrowIconButtonProps> = ({ className, action, title, iconClassName }) => {
|
||||
return (
|
||||
<IconButton
|
||||
className={className}
|
||||
data-action={action}
|
||||
title={globalize.translate(title)}
|
||||
>
|
||||
<PlayArrowIcon className={iconClassName} />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayArrowIconButton;
|
22
src/components/common/PlaylistAddIconButton.tsx
Normal file
22
src/components/common/PlaylistAddIconButton.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React, { FC } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
interface PlaylistAddIconButtonProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const PlaylistAddIconButton: FC<PlaylistAddIconButtonProps> = ({ className }) => {
|
||||
return (
|
||||
<IconButton
|
||||
className={className}
|
||||
data-action='addtoplaylist'
|
||||
title={globalize.translate('AddToPlaylist')}
|
||||
>
|
||||
<PlaylistAddIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlaylistAddIconButton;
|
24
src/components/common/RightIconButtons.tsx
Normal file
24
src/components/common/RightIconButtons.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React, { FC } from 'react';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
|
||||
interface RightIconButtonsProps {
|
||||
className?: string;
|
||||
id: string;
|
||||
icon: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const RightIconButtons: FC<RightIconButtonsProps> = ({ className, id, title, icon }) => {
|
||||
return (
|
||||
<IconButton
|
||||
className={className}
|
||||
data-action='custom'
|
||||
data-customaction={id}
|
||||
title={title}
|
||||
>
|
||||
{icon}
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default RightIconButtons;
|
48
src/types/dataAttributes.ts
Normal file
48
src/types/dataAttributes.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import type { CollectionType, UserItemDataDto } from '@jellyfin/sdk/lib/generated-client';
|
||||
import type { NullableBoolean, NullableNumber, NullableString } from './itemDto';
|
||||
|
||||
export type AttributesOpts = {
|
||||
context?: CollectionType | undefined,
|
||||
parentId?: NullableString,
|
||||
collectionId?: NullableString,
|
||||
playlistId?: NullableString,
|
||||
prefix?: NullableString,
|
||||
action?: NullableString,
|
||||
itemServerId?: NullableString,
|
||||
itemId?: NullableString,
|
||||
itemTimerId?: NullableString,
|
||||
itemSeriesTimerId?: NullableString,
|
||||
itemChannelId?: NullableString,
|
||||
itemPlaylistItemId?: NullableString,
|
||||
itemType?: NullableString,
|
||||
itemMediaType?: NullableString,
|
||||
itemCollectionType?: NullableString,
|
||||
itemIsFolder?: NullableBoolean,
|
||||
itemPath?: NullableString,
|
||||
itemStartDate?: NullableString,
|
||||
itemEndDate?: NullableString,
|
||||
itemUserData?: UserItemDataDto
|
||||
};
|
||||
|
||||
export type DataAttributes = {
|
||||
'data-playlistitemid'?: NullableString;
|
||||
'data-timerid'?: NullableString;
|
||||
'data-seriestimerid'?: NullableString;
|
||||
'data-serverid'?: NullableString;
|
||||
'data-id'?: NullableString;
|
||||
'data-type'?: NullableString;
|
||||
'data-collectionid'?: NullableString;
|
||||
'data-playlistid'?: NullableString;
|
||||
'data-mediatype'?: NullableString;
|
||||
'data-channelid'?: NullableString;
|
||||
'data-path'?: NullableString;
|
||||
'data-collectiontype'?: NullableString;
|
||||
'data-context'?: NullableString;
|
||||
'data-parentid'?: NullableString;
|
||||
'data-startdate'?: NullableString;
|
||||
'data-enddate'?: NullableString;
|
||||
'data-prefix'?: NullableString;
|
||||
'data-action'?: NullableString;
|
||||
'data-positionticks'?: NullableNumber;
|
||||
'data-isfolder'?: NullableBoolean;
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client';
|
||||
import type { DeviceInfo } from '@jellyfin/sdk/lib/generated-client/models/device-info';
|
||||
import type { SessionInfo } from '@jellyfin/sdk/lib/generated-client/models/session-info';
|
||||
|
||||
@ -103,7 +104,41 @@ export function getLibraryIcon(library: string | null | undefined) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getItemTypeIcon(itemType: BaseItemKind | string) {
|
||||
switch (itemType) {
|
||||
case BaseItemKind.MusicAlbum:
|
||||
return 'album';
|
||||
case BaseItemKind.MusicArtist:
|
||||
case BaseItemKind.Person:
|
||||
return 'person';
|
||||
case BaseItemKind.Audio:
|
||||
return 'audiotrack';
|
||||
case BaseItemKind.Movie:
|
||||
return 'movie';
|
||||
case BaseItemKind.Episode:
|
||||
case BaseItemKind.Series:
|
||||
return 'tv';
|
||||
case BaseItemKind.Program:
|
||||
return 'live_tv';
|
||||
case BaseItemKind.Book:
|
||||
return 'book';
|
||||
case BaseItemKind.Folder:
|
||||
return 'folder';
|
||||
case BaseItemKind.BoxSet:
|
||||
return 'collections';
|
||||
case BaseItemKind.Playlist:
|
||||
return 'view_list';
|
||||
case BaseItemKind.Photo:
|
||||
return 'photo';
|
||||
case BaseItemKind.PhotoAlbum:
|
||||
return 'photo_album';
|
||||
default:
|
||||
return 'folder';
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getDeviceIcon,
|
||||
getLibraryIcon
|
||||
getLibraryIcon,
|
||||
getItemTypeIcon
|
||||
};
|
||||
|
@ -3,8 +3,10 @@ import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type'
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
||||
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { EpisodeFilter, FeatureFilters, LibraryViewSettings, ParentId, VideoBasicFilter, ViewMode } from '../types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
import type { AttributesOpts, DataAttributes } from 'types/dataAttributes';
|
||||
|
||||
export const getVideoBasicFilter = (libraryViewSettings: LibraryViewSettings) => {
|
||||
let isHd;
|
||||
@ -164,3 +166,31 @@ export const getDefaultLibraryViewSettings = (viewType: LibraryTab): LibraryView
|
||||
StartIndex: 0
|
||||
};
|
||||
};
|
||||
|
||||
export function getDataAttributes(
|
||||
opts: AttributesOpts
|
||||
): DataAttributes {
|
||||
return {
|
||||
'data-context': opts.context,
|
||||
'data-collectionid': opts.collectionId,
|
||||
'data-playlistid': opts.playlistId,
|
||||
'data-parentid': opts.parentId,
|
||||
'data-playlistitemid': opts.itemPlaylistItemId,
|
||||
'data-action': layoutManager.tv ? opts.action : null,
|
||||
'data-serverid': opts.itemServerId,
|
||||
'data-id': opts.itemId,
|
||||
'data-timerid': opts.itemTimerId,
|
||||
'data-seriestimerid': opts.itemSeriesTimerId,
|
||||
'data-channelid': opts.itemChannelId,
|
||||
'data-type': opts.itemType,
|
||||
'data-mediatype': opts.itemMediaType,
|
||||
'data-collectiontype': opts.itemCollectionType,
|
||||
'data-isfolder': opts.itemIsFolder,
|
||||
'data-path': opts.itemPath,
|
||||
'data-prefix': opts.prefix,
|
||||
'data-positionticks': opts.itemUserData?.PlaybackPositionTicks,
|
||||
'data-startdate': opts.itemStartDate?.toString(),
|
||||
'data-enddate': opts.itemEndDate?.toString()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -200,6 +200,8 @@ const config = {
|
||||
path.resolve(__dirname, 'node_modules/markdown-it'),
|
||||
path.resolve(__dirname, 'node_modules/mdurl'),
|
||||
path.resolve(__dirname, 'node_modules/punycode'),
|
||||
path.resolve(__dirname, 'node_modules/react-blurhash'),
|
||||
path.resolve(__dirname, 'node_modules/react-lazy-load-image-component'),
|
||||
path.resolve(__dirname, 'node_modules/react-router'),
|
||||
path.resolve(__dirname, 'node_modules/screenfull'),
|
||||
path.resolve(__dirname, 'node_modules/ssr-window'),
|
||||
|
Loading…
x
Reference in New Issue
Block a user