Files
abyss-lab/src/pages/honkai3rd/battlesuits/index.tsx
T
2023-05-23 19:17:05 +09:00

238 lines
7.2 KiB
TypeScript

/** @jsxImportSource theme-ui */
import { Box, Heading, Flex, Button } from '@theme-ui/components'
import { useCallback, useMemo, useState } from 'react'
import FilterButton from '../../../components/atoms/FilterButton'
import Breadcrumb from '../../../components/organisms/Breadcrumb'
import { listBattlesuits } from '../../../server/data/honkai3rd/battlesuits'
import { BattlesuitData, battlesuitFeatures, battlesuitTypes, valkyries } from '../../../lib/honkai3rd/battlesuits'
import BattlesuitCard from '../../../components/molecules/BattlesuitCard'
import { useRouter } from 'next/router'
import { NextPageContext } from 'next'
import { getI18NProps } from '../../../server/i18n'
import { translate, useTranslation } from '../../../lib/i18n'
import Head from '../../../components/atoms/Head'
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
type BattlesuitListItemData = Pick<BattlesuitData, 'id' | 'name' | 'features' | 'type' | 'valkyrie'>
interface BattlesuitListPageProps {
battlesuits: BattlesuitListItemData[]
}
const featureFilterOptions: {
value: string
label: string
icon?: string
krLabel?: string
}[] = [{ value: 'all', label: 'All', krLabel: '전체' }, ...battlesuitFeatures]
const valkyrieFilterOptions: {
value: string
label: string
icon?: string
krLabel?: string
}[] = [{ value: 'all', label: 'All', krLabel: '전체' }, ...battlesuitTypes, ...valkyries]
const BattlesuitListPage = ({ battlesuits }: BattlesuitListPageProps) => {
const { query, locale } = useRouter()
const { t } = useTranslation()
const [hiddenFilters, setHiddenFilters] = useState(true)
const closeFilters = useCallback(() => {
setHiddenFilters(true)
}, [])
const valkyrieFilter = useMemo(() => {
if (query.valkyrie == null) {
return 'all'
}
return typeof query.valkyrie === 'string' ? query.valkyrie : query.valkyrie[0]
}, [query])
const featureFilter = useMemo(() => {
if (query.feature == null) {
return 'all'
}
return typeof query.feature === 'string' ? query.feature : query.feature[0]
}, [query])
const battlesuitsFiteredByValkyrie = useMemo(() => {
return battlesuits.filter(battlesuit => isValkyrieFilterMatching(battlesuit, valkyrieFilter))
}, [battlesuits, valkyrieFilter])
const availableFeatureSet = battlesuitsFiteredByValkyrie.reduce((set, battlesuit) => {
battlesuit.features.forEach(feature => set.add(feature))
return set
}, new Set())
const battlesuitsFilteredByFeature = useMemo(() => {
return battlesuitsFiteredByValkyrie.filter(battlesuit => isFeatureFilterMatching(battlesuit, featureFilter))
}, [battlesuitsFiteredByValkyrie, featureFilter])
return (
<Honkai3rdLayout>
<Head
title={`${t('common.battlesuits')} - ${t('common.honkai-3rd')} - ${t('common.abyss-lab')}`}
description={t('battlesuits-list.description')}
canonicalHref={`/honkai3rd/battlesuits`}
/>
<Box p={3}>
<Breadcrumb
items={[
{ href: '/honkai3rd', label: t('common.honkai-3rd') },
{
href: '/honkai3rd/battlesuits',
label: t('common.battlesuits')
}
]}
/>
<Heading as="h1">{t('battlesuits-list.heading')}</Heading>
<Box sx={{ display: ['block', 'none'], mb: 2 }}>
<Button
className={hiddenFilters ? '' : 'active'}
sx={{ px: 2, py: 1 }}
onClick={() => {
setHiddenFilters(previousValue => !previousValue)
}}
>
{t('battlesuits-list.filters')}
</Button>
</Box>
<Box
className={hiddenFilters ? 'hidden' : ''}
sx={{
display: 'block',
'&.hidden': {
display: ['none', 'block']
}
}}
>
<Heading as="h3">{t('battlesuits-list.filter-by-valkyries')}</Heading>
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
{valkyrieFilterOptions.map(({ value, label, icon, krLabel }) => {
return (
<FilterButton
key={value}
active={value === valkyrieFilter}
href={`?valkyrie=${value}`}
label={translate(locale, { 'ko-KR': krLabel }, label)}
icon={icon}
m={1}
close={closeFilters}
/>
)
})}
</Flex>
<Heading as="h3">{t('battlesuits-list.filter-by-features')}</Heading>
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
{featureFilterOptions
.filter(filterOption => {
if (filterOption.value === 'all') {
return true
}
return availableFeatureSet.has(filterOption.value)
})
.map(({ value, label, icon, krLabel }) => {
return (
<FilterButton
key={value}
active={value === featureFilter}
href={`?valkyrie=${valkyrieFilter}&feature=${value}`}
label={translate(locale, { 'ko-KR': krLabel }, label)}
icon={icon}
m={1}
close={closeFilters}
/>
)
})}
</Flex>
</Box>
<Flex
sx={{
flexWrap: 'wrap',
justifyContent: 'space-around'
}}
>
{battlesuitsFilteredByFeature.map(battlesuit => {
return <BattlesuitCard key={battlesuit.id} battlesuit={battlesuit} />
})}
</Flex>
</Box>
</Honkai3rdLayout>
)
}
export default BattlesuitListPage
export async function getStaticProps({ locale }: NextPageContext) {
return {
props: {
battlesuits: listBattlesuits(locale).map(battlesuit => {
return {
id: battlesuit.id,
name: battlesuit.name,
features: battlesuit.features,
type: battlesuit.type,
valkyrie: battlesuit.valkyrie
}
}),
...(await getI18NProps(locale))
}
}
}
function isValkyrieFilterMatching(battlesuit: BattlesuitListItemData, valkyrieFilter: string) {
switch (valkyrieFilter) {
case 'mecha':
case 'biologic':
case 'psychic':
case 'quantum':
case 'imaginary':
return battlesuit.type === valkyrieFilter
case 'kiana':
case 'mei':
case 'bronya':
case 'himeko':
case 'theresa':
case 'fuhua':
case 'rita':
case 'sakura':
case 'kallen':
case 'olenyevas':
case 'seele':
case 'durandal':
case 'fischl':
case 'elysia':
case 'mobius':
case 'raven':
case 'carole':
case 'asuka':
case 'pardofelis':
case 'aponia':
case 'eden':
case 'griseo':
case 'sushang':
case 'ai':
case 'susannah':
case 'misteln':
case 'prometheus':
return battlesuit.valkyrie === valkyrieFilter
default:
case 'all':
return true
}
}
function isFeatureFilterMatching(battlesuit: BattlesuitListItemData, featureFilter: string) {
if (featureFilter === 'all') {
return true
}
return battlesuit.features.some(feature => feature === featureFilter)
}