238 lines
7.2 KiB
TypeScript
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)
|
|
}
|