forked from ShokoAnime/Shoko-WebUI
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from fearnlj01/series-redesign
Tag filtering within series pages, Group management in Edit Series Modal
- Loading branch information
Showing
25 changed files
with
947 additions
and
545 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
src/components/Collection/Credits/CreditsSearchAndFilterPanel.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import React, {} from 'react'; | ||
import { mdiInformationOutline, mdiMagnify, mdiPlayCircleOutline } from '@mdi/js'; | ||
import Icon from '@mdi/react'; | ||
|
||
import Checkbox from '@/components/Input/Checkbox'; | ||
import Input from '@/components/Input/Input'; | ||
import ShokoPanel from '@/components/Panels/ShokoPanel'; | ||
|
||
type Props = { | ||
inputPlaceholder: string; | ||
search: string; | ||
handleSearchChange: (event: React.ChangeEvent<HTMLInputElement>) => void; | ||
roleFilter: Set<string>; | ||
handleFilterChange: (event: React.ChangeEvent<HTMLInputElement>) => void; | ||
uniqueRoles: string[]; | ||
refreshAniDbAction: () => void; | ||
aniDbRefreshing: boolean; | ||
refreshTvDbAction: () => void; | ||
tvDbRefreshing: boolean; | ||
}; | ||
const CreditsSearchAndFilterPanel = React.memo( | ||
( | ||
{ | ||
aniDbRefreshing, | ||
handleFilterChange, | ||
handleSearchChange, | ||
inputPlaceholder, | ||
refreshAniDbAction, | ||
refreshTvDbAction, | ||
roleFilter, | ||
search, | ||
tvDbRefreshing, | ||
uniqueRoles, | ||
}: Props, | ||
) => ( | ||
<ShokoPanel | ||
title="Search & Filter" | ||
className="w-400" | ||
contentClassName="gap-y-6" | ||
sticky | ||
transparent | ||
fullHeight={false} | ||
> | ||
<div className="flex flex-col gap-y-2"> | ||
<span className="flex w-full text-base font-semibold"> | ||
Name | ||
</span> | ||
<Input | ||
id="search" | ||
startIcon={mdiMagnify} | ||
type="text" | ||
placeholder={inputPlaceholder} | ||
value={search} | ||
inputClassName="px-4 py-3" | ||
onChange={handleSearchChange} | ||
/> | ||
</div> | ||
<div className="flex flex-col gap-y-2"> | ||
<div className="text-base font-semibold">Roles</div> | ||
<div className="flex flex-col gap-y-2 rounded-lg bg-panel-input p-6"> | ||
{uniqueRoles.map(desc => ( | ||
<Checkbox | ||
justify | ||
label={desc} | ||
key={desc} | ||
id={desc} | ||
isChecked={!roleFilter.has(desc)} | ||
onChange={handleFilterChange} | ||
/> | ||
))} | ||
</div> | ||
</div> | ||
<div className="flex flex-col gap-y-2"> | ||
<div className="text-base font-semibold">Quick Actions</div> | ||
<button | ||
type="button" | ||
className="flex w-full flex-row justify-between disabled:cursor-not-allowed disabled:opacity-65" | ||
onClick={refreshAniDbAction} | ||
disabled={aniDbRefreshing} | ||
> | ||
Force refresh: AniDB | ||
<Icon | ||
path={mdiPlayCircleOutline} | ||
className="pointer-events-auto text-panel-icon-action group-disabled:cursor-not-allowed" | ||
size={1} | ||
/> | ||
</button> | ||
<button | ||
type="button" | ||
className="flex w-full flex-row justify-between disabled:cursor-not-allowed disabled:opacity-65" | ||
onClick={refreshTvDbAction} | ||
disabled={tvDbRefreshing} | ||
> | ||
Force refresh: TVDB | ||
<Icon | ||
path={mdiPlayCircleOutline} | ||
className="pointer-events-auto text-panel-icon-action group-disabled:cursor-not-allowed" | ||
size={1} | ||
/> | ||
</button> | ||
</div> | ||
<hr className="border border-panel-border" /> | ||
<div className="flex flex-row gap-x-3"> | ||
<Icon path={mdiInformationOutline} className="text-panel-icon-warning" size={1} /> | ||
<div className="grow text-base font-semibold"> | ||
Warning! Possible Spoilers | ||
</div> | ||
</div> | ||
</ShokoPanel> | ||
), | ||
); | ||
|
||
export default CreditsSearchAndFilterPanel; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import React from 'react'; | ||
|
||
import CharacterImage from '@/components/CharacterImage'; | ||
|
||
import type { SeriesCast } from '@/core/types/api/series'; | ||
import type { CreditsModeType } from '@/pages/collection/series/SeriesCredits'; | ||
|
||
const getThumbnailUrl = (item: SeriesCast, mode: CreditsModeType) => { | ||
const thumbnail = item[mode].Image ?? null; | ||
if (thumbnail === null) return null; | ||
return `/api/v3/Image/${thumbnail.Source}/${thumbnail.Type}/${thumbnail.ID}`; | ||
}; | ||
|
||
const CreditsStaffPanel = React.memo(({ cast, mode }: { cast: SeriesCast, mode: CreditsModeType }) => ( | ||
<div className="flex w-full flex-row items-center gap-6 rounded-lg border border-panel-border bg-panel-background-transparent p-6 font-semibold"> | ||
<div className="z-10 flex gap-x-2"> | ||
{mode === 'Character' && ( | ||
<CharacterImage | ||
imageSrc={getThumbnailUrl(cast, 'Character')} | ||
className="relative h-[7.75rem] w-[6.063rem] rounded-lg" | ||
/> | ||
)} | ||
<CharacterImage | ||
imageSrc={getThumbnailUrl(cast, 'Staff')} | ||
className="relative h-[7.75rem] w-[6.063rem] rounded-lg" | ||
/> | ||
</div> | ||
<div className="grow text-center"> | ||
<div className="line-clamp-2 text-base leading-8 xl:text-xl" title={cast[mode]?.Name}> | ||
{cast[mode]?.Name} | ||
</div> | ||
{mode === 'Character' && <div className="opacity-65">{cast.Staff?.Name}</div>} | ||
<div className="mt-2 text-sm">{cast.RoleDetails}</div> | ||
</div> | ||
</div> | ||
)); | ||
|
||
export default CreditsStaffPanel; |
43 changes: 43 additions & 0 deletions
43
src/components/Collection/Credits/CreditsStaffVirtualizer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import React from 'react'; | ||
import { useOutletContext } from 'react-router'; | ||
import { useVirtualizer } from '@tanstack/react-virtual'; | ||
|
||
import CreditsStaffPanel from '@/components/Collection/Credits/CreditsStaffPanel'; | ||
|
||
import type { SeriesCast } from '@/core/types/api/series'; | ||
import type { CreditsModeType } from '@/pages/collection/series/SeriesCredits'; | ||
|
||
const StaffPanelVirtualizer = ({ castArray, mode }: { castArray: SeriesCast[], mode: CreditsModeType }) => { | ||
const { scrollRef } = useOutletContext<{ scrollRef: React.RefObject<HTMLDivElement> }>(); | ||
const cardSize = { x: 466.5, y: 174, gap: 24 }; | ||
|
||
const rowVirtualizer = useVirtualizer({ | ||
count: castArray.length, | ||
getScrollElement: () => scrollRef.current, | ||
estimateSize: () => cardSize.y, | ||
overscan: 30, // Greater than the norm as lanes aren't taken into account | ||
lanes: 3, | ||
gap: cardSize.gap, | ||
}); | ||
|
||
return ( | ||
<div className="relative w-full" style={{ height: rowVirtualizer.getTotalSize() }}> | ||
{rowVirtualizer.getVirtualItems().map(virtualRow => ( | ||
<div | ||
key={virtualRow.key} | ||
className="absolute top-0" | ||
style={{ | ||
left: virtualRow.lane * (cardSize.x + cardSize.gap), | ||
width: cardSize.x, | ||
height: cardSize.y, | ||
transform: `translateY(${virtualRow.start}px)`, | ||
}} | ||
> | ||
<CreditsStaffPanel cast={castArray[virtualRow.index]} mode={mode} /> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
}; | ||
|
||
export default StaffPanelVirtualizer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.