Implement battlesuit filters
This commit is contained in:
@@ -1,26 +1,17 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import { useRouter } from 'next/router'
|
||||
import { Link, Text } from 'theme-ui'
|
||||
import { assetsBucketBaseUrl } from '../../lib/consts'
|
||||
import SquareImageBox from './SquareImageBox'
|
||||
import { ReactNode } from 'react'
|
||||
import { Link } from 'theme-ui'
|
||||
|
||||
interface FilterButtonProps {
|
||||
active?: boolean
|
||||
icon?: string
|
||||
label: string
|
||||
href: string
|
||||
m?: string | number
|
||||
close?: () => void
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const FilterButton = ({
|
||||
active = false,
|
||||
icon,
|
||||
label,
|
||||
href,
|
||||
m,
|
||||
close,
|
||||
}: FilterButtonProps) => {
|
||||
const FilterButton = ({ active = false, children, href, m }: FilterButtonProps) => {
|
||||
const { push } = useRouter()
|
||||
return (
|
||||
<Link
|
||||
@@ -30,24 +21,13 @@ const FilterButton = ({
|
||||
px={2}
|
||||
className={active ? 'active' : ''}
|
||||
sx={{ display: 'flex' }}
|
||||
variant='buttons.primary'
|
||||
onClick={(event) => {
|
||||
variant="buttons.primary"
|
||||
onClick={event => {
|
||||
event.preventDefault()
|
||||
if (close != null) {
|
||||
close()
|
||||
}
|
||||
push(href, href, { shallow: true })
|
||||
}}
|
||||
>
|
||||
{icon != null && (
|
||||
<SquareImageBox
|
||||
src={`${assetsBucketBaseUrl}/honkai3rd/${icon}.png`}
|
||||
alt={label}
|
||||
size={30}
|
||||
mr={1}
|
||||
/>
|
||||
)}
|
||||
<Text sx={{ lineHeight: '30px' }}>{label}</Text>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
+144
-6
@@ -84,7 +84,89 @@ export function getSkillTypeLabel(type: SkillType) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getCharacterTypeLabel(type: CharacterType) {
|
||||
export function getCharacterTypeLabel(type: CharacterType, locale: string = 'en-US') {
|
||||
if (locale === 'en-US') {
|
||||
switch (type) {
|
||||
case 'kiana':
|
||||
return 'Kiana'
|
||||
case 'mei':
|
||||
return 'Mei'
|
||||
case 'bronya':
|
||||
return 'Bronya'
|
||||
case 'himeko':
|
||||
return 'Himeko'
|
||||
case 'theresa':
|
||||
return 'Theresa'
|
||||
case 'fuhua':
|
||||
return 'Fu Hua'
|
||||
case 'rita':
|
||||
return 'Rita'
|
||||
case 'sakura':
|
||||
return 'Sakura'
|
||||
case 'kallen':
|
||||
return 'Kallen'
|
||||
case 'olenyevas':
|
||||
return 'Olenyevas'
|
||||
case 'seele':
|
||||
return 'Seele'
|
||||
case 'durandal':
|
||||
return 'Durandal'
|
||||
case 'fischl':
|
||||
return 'Fischl'
|
||||
case 'elysia':
|
||||
return 'Elysia'
|
||||
case 'mobius':
|
||||
return 'Mobius'
|
||||
case 'raven':
|
||||
return 'Raven'
|
||||
case 'carole':
|
||||
return 'Carole'
|
||||
case 'pardofelis':
|
||||
return 'Pardofelis'
|
||||
case 'aponia':
|
||||
return 'Aponia'
|
||||
case 'eden':
|
||||
return 'Eden'
|
||||
case 'griseo':
|
||||
return 'Griseo'
|
||||
case 'vill-v':
|
||||
return 'Vill V'
|
||||
case 'sushang':
|
||||
return 'Sushang'
|
||||
case 'ai':
|
||||
return 'AI'
|
||||
case 'susannah':
|
||||
return 'Susannah'
|
||||
case 'hare':
|
||||
return 'Hare'
|
||||
case 'prometheus':
|
||||
return 'Prometheus'
|
||||
case 'kira':
|
||||
return 'Kira'
|
||||
case 'asuka':
|
||||
return 'Asuka'
|
||||
|
||||
// Event Only
|
||||
case 'keqing':
|
||||
return 'Keqing'
|
||||
|
||||
// APHO
|
||||
|
||||
case 'apho-mei':
|
||||
return '후서 메이'
|
||||
case 'apho-adam':
|
||||
return '후서 아담'
|
||||
case 'apho-carol':
|
||||
return '후서 캐롤'
|
||||
case 'apho-bronya':
|
||||
return '후서 브로냐'
|
||||
case 'apho-timido':
|
||||
return '후서 티미도'
|
||||
|
||||
default:
|
||||
return `Unknown Character Type (${type})`
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case 'kiana':
|
||||
return '키아나'
|
||||
@@ -167,7 +249,63 @@ export function getCharacterTypeLabel(type: CharacterType) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getTagTypeLabel(type: TagType) {
|
||||
export function getTagTypeLabel(type: TagType, locale: string = 'en-US') {
|
||||
if (type) {
|
||||
switch (type) {
|
||||
case 'branch':
|
||||
return 'Branch'
|
||||
case 'charge':
|
||||
return 'Charge'
|
||||
case 'physical-dmg':
|
||||
return 'Physical DMG'
|
||||
case 'fire-dmg':
|
||||
return 'Fire DMG'
|
||||
case 'ice-dmg':
|
||||
return 'Ice DMG'
|
||||
case 'lightning-dmg':
|
||||
return 'Lightning DMG'
|
||||
case 'freeze':
|
||||
return 'Freeze'
|
||||
case 'paralyze':
|
||||
return 'Paralyze'
|
||||
case 'stun':
|
||||
return 'Stun'
|
||||
case 'ignite':
|
||||
return 'Ignite'
|
||||
case 'bleed':
|
||||
return 'Bleed'
|
||||
case 'heavy-atk':
|
||||
return 'Heavy ATK'
|
||||
case 'weaken':
|
||||
return 'Weaken'
|
||||
case 'impair':
|
||||
return 'Impair'
|
||||
case 'float':
|
||||
return 'Float'
|
||||
case 'slow-down':
|
||||
return 'Slow Down'
|
||||
case 'time-mastery':
|
||||
return 'Time Mastery'
|
||||
case 'gather':
|
||||
return 'Gather'
|
||||
case 'heal':
|
||||
return 'Heal'
|
||||
case 'fast-atk':
|
||||
return 'Fast ATK'
|
||||
case 'aerial':
|
||||
return 'Aerial'
|
||||
case 'burst':
|
||||
return 'Burst'
|
||||
case 'shield':
|
||||
return 'Shield'
|
||||
case 'meele':
|
||||
return 'Meele'
|
||||
case 'ranged':
|
||||
return 'Ranged'
|
||||
default:
|
||||
return `Unknown Tag Type (${type})`
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case 'branch':
|
||||
return '분기'
|
||||
@@ -210,11 +348,11 @@ export function getTagTypeLabel(type: TagType) {
|
||||
case 'fast-atk':
|
||||
return '높은 빈도'
|
||||
case 'aerial':
|
||||
return '버스트'
|
||||
case 'burst':
|
||||
return '실드'
|
||||
case 'shield':
|
||||
return '대공'
|
||||
case 'burst':
|
||||
return '버스트'
|
||||
case 'shield':
|
||||
return '실드'
|
||||
case 'meele':
|
||||
return '근접'
|
||||
case 'ranged':
|
||||
|
||||
@@ -103,7 +103,7 @@ export interface Battlesuit {
|
||||
|
||||
export type BattlesuitCatalogItem = Pick<
|
||||
Battlesuit,
|
||||
'id' | 'fullName' | 'attributeType' | 'weapon' | 'character' | 'initialStar'
|
||||
'id' | 'fullName' | 'attributeType' | 'weapon' | 'character' | 'initialStar' | 'tags'
|
||||
>
|
||||
|
||||
export interface SkillTagItem {
|
||||
|
||||
@@ -1,7 +1,61 @@
|
||||
import { BattlesuitSkill } from './types'
|
||||
import { BattlesuitSkill, CharacterType, TagType } from './types'
|
||||
|
||||
export function sortBattlesuitSkill(a: BattlesuitSkill, b: BattlesuitSkill) {
|
||||
return reversedSkillTypeOrder.indexOf(b.skillType) - reversedSkillTypeOrder.indexOf(a.skillType)
|
||||
}
|
||||
|
||||
const reversedSkillTypeOrder = ['leader', 'passive', 'evasion', 'special', 'ultimate', 'basic', 'sp', 'none'].reverse()
|
||||
|
||||
export const characterFilterTypes: CharacterType[] = [
|
||||
'kiana',
|
||||
'mei',
|
||||
'bronya',
|
||||
'himeko',
|
||||
'theresa',
|
||||
'fuhua',
|
||||
'rita',
|
||||
'sakura',
|
||||
'kallen',
|
||||
'olenyevas',
|
||||
'seele',
|
||||
'durandal',
|
||||
'fischl',
|
||||
'elysia',
|
||||
'mobius',
|
||||
'raven',
|
||||
'carole',
|
||||
'pardofelis',
|
||||
'aponia',
|
||||
'eden',
|
||||
'griseo',
|
||||
'vill-v',
|
||||
'sushang',
|
||||
'ai',
|
||||
'susannah',
|
||||
'hare',
|
||||
'prometheus',
|
||||
'kira',
|
||||
'asuka',
|
||||
'keqing'
|
||||
]
|
||||
|
||||
export const tagFilterTypes: TagType[] = [
|
||||
'physical-dmg',
|
||||
'fire-dmg',
|
||||
'ice-dmg',
|
||||
'lightning-dmg',
|
||||
'freeze',
|
||||
'paralyze',
|
||||
'stun',
|
||||
'ignite',
|
||||
'bleed',
|
||||
'heavy-atk',
|
||||
'weaken',
|
||||
'impair',
|
||||
'time-mastery',
|
||||
'gather',
|
||||
'heal',
|
||||
'fast-atk',
|
||||
'aerial',
|
||||
'burst'
|
||||
]
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
/** @jsxImportSource theme-ui */
|
||||
import { Box } from '@theme-ui/components'
|
||||
import { Box, Heading } from '@theme-ui/components'
|
||||
import { NextPageContext } from 'next'
|
||||
import { Flex, Link } from 'theme-ui'
|
||||
import { Flex, Link, Text } from 'theme-ui'
|
||||
import { loadBattlesuitCatalog } from '../../../lib/v2/server/loadData'
|
||||
import { BattlesuitCatalogItem } from '../../../lib/v2/data/types'
|
||||
import { BattlesuitCatalogItem, TagType } from '../../../lib/v2/data/types'
|
||||
import BattlesuitCatalogItemCard from '../../../components/v2/BattlesuitCatalogItemCard'
|
||||
import Head from '../../../components/atoms/Head'
|
||||
import Honkai3rdLayout from '../../../components/layouts/Honkai3rdLayout'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Breadcrumb from '../../../components/organisms/Breadcrumb'
|
||||
import { getI18NProps } from '../../../server/i18n'
|
||||
import FilterButton from '../../../components/atoms/FilterButton'
|
||||
import { translate } from '../../../lib/i18n'
|
||||
import { characterFilterTypes } from '../../../lib/v2/data/utils'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useMemo } from 'react'
|
||||
import { getCharacterTypeLabel, getTagTypeLabel } from '../../../lib/v2/data/text'
|
||||
import ChibiIcon from '../../../components/v2/ChibiIcon'
|
||||
import TagIcon from '../../../components/v2/TagIcon'
|
||||
|
||||
interface BattlesuitListPageProps {
|
||||
battlesuitCatalog: BattlesuitCatalogItem[]
|
||||
@@ -17,6 +25,42 @@ interface BattlesuitListPageProps {
|
||||
|
||||
const BattlesuitListPage = ({ battlesuitCatalog }: BattlesuitListPageProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale, query } = useRouter()
|
||||
|
||||
const valkyrieFilter = useMemo(() => {
|
||||
if (query.valkyrie == null) {
|
||||
return 'all'
|
||||
}
|
||||
|
||||
return typeof query.valkyrie === 'string' ? query.valkyrie : query.valkyrie[0]
|
||||
}, [query])
|
||||
|
||||
const tagFilter = useMemo(() => {
|
||||
if (query.tag == null) {
|
||||
return 'all'
|
||||
}
|
||||
|
||||
return typeof query.tag === 'string' ? query.tag : query.tag[0]
|
||||
}, [query])
|
||||
|
||||
const battlesuitsFilteredByValkyrie = useMemo(() => {
|
||||
return battlesuitCatalog.filter(battlesuit => {
|
||||
if (valkyrieFilter === 'all') {
|
||||
return true
|
||||
}
|
||||
|
||||
return valkyrieFilter === battlesuit.character
|
||||
})
|
||||
}, [battlesuitCatalog, valkyrieFilter])
|
||||
|
||||
const tagFilterTypes = useMemo(() => {
|
||||
const tagSet = battlesuitsFilteredByValkyrie.reduce<Set<TagType>>((set, battlesuit) => {
|
||||
battlesuit.tags.forEach(tag => set.add(tag))
|
||||
return set
|
||||
}, new Set())
|
||||
return [...tagSet]
|
||||
}, [battlesuitsFilteredByValkyrie])
|
||||
|
||||
return (
|
||||
<Honkai3rdLayout>
|
||||
<Head
|
||||
@@ -34,18 +78,72 @@ const BattlesuitListPage = ({ battlesuitCatalog }: BattlesuitListPageProps) => {
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<h1>{t('battlesuits-list.heading')}</h1>
|
||||
<Flex sx={{ flexWrap: 'wrap' }}>
|
||||
{battlesuitCatalog.map(battlesuit => {
|
||||
|
||||
<Heading as="h1">{t('battlesuits-list.heading')}</Heading>
|
||||
|
||||
<Heading as="h3">{t('battlesuits-list.filter-by-valkyries')}</Heading>
|
||||
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
|
||||
{characterFilterTypes.map(type => {
|
||||
const active = type === valkyrieFilter
|
||||
return (
|
||||
<Box key={battlesuit.id}>
|
||||
<Link href={`/honkai3rd/battlesuits/${battlesuit.id}`}>
|
||||
<BattlesuitCatalogItemCard battlesuit={battlesuit} />
|
||||
</Link>
|
||||
</Box>
|
||||
<FilterButton key={type} active={active} href={active ? `` : `?valkyrie=${type}`} m={1}>
|
||||
<Flex sx={{ alignItems: 'center' }}>
|
||||
<ChibiIcon id={type} />
|
||||
<Text ml={1}>
|
||||
{translate(locale, { 'ko-KR': getCharacterTypeLabel(type, 'ko-KR') }, getCharacterTypeLabel(type))}
|
||||
</Text>
|
||||
</Flex>
|
||||
</FilterButton>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
|
||||
{tagFilterTypes.length > 0 && (
|
||||
<>
|
||||
<Heading as="h3">{t('battlesuits-list.filter-by-features')}</Heading>
|
||||
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
|
||||
{tagFilterTypes.map(type => {
|
||||
const active = type === tagFilter
|
||||
return (
|
||||
<FilterButton
|
||||
key={type}
|
||||
active={active}
|
||||
href={`?${valkyrieFilter !== 'all' ? `valkyrie=${valkyrieFilter}&` : ''}${
|
||||
active ? '' : `tag=${type}`
|
||||
}`}
|
||||
m={1}
|
||||
>
|
||||
<Flex sx={{ alignItems: 'center' }}>
|
||||
<TagIcon type={type} />
|
||||
<Text ml={1}>
|
||||
{translate(locale, { 'ko-KR': getTagTypeLabel(type, 'ko-KR') }, getTagTypeLabel(type))}
|
||||
</Text>
|
||||
</Flex>
|
||||
</FilterButton>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Flex sx={{ flexWrap: 'wrap' }}>
|
||||
{battlesuitsFilteredByValkyrie
|
||||
.filter(battlesuit => {
|
||||
if (tagFilter === 'all') {
|
||||
return true
|
||||
}
|
||||
return battlesuit.tags.findIndex(tag => tag === tagFilter) > -1
|
||||
})
|
||||
.map(battlesuit => {
|
||||
return (
|
||||
<Box key={battlesuit.id}>
|
||||
<Link href={`/honkai3rd/battlesuits/${battlesuit.id}`}>
|
||||
<BattlesuitCatalogItemCard battlesuit={battlesuit} />
|
||||
</Link>
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</Box>
|
||||
</Honkai3rdLayout>
|
||||
)
|
||||
|
||||
@@ -58,7 +58,8 @@ function writeBattlesuitData(locale: string) {
|
||||
attributeType: battlesuit.attributeType,
|
||||
weapon: battlesuit.weapon,
|
||||
character: battlesuit.character,
|
||||
initialStar: battlesuit.initialStar
|
||||
initialStar: battlesuit.initialStar,
|
||||
tags: battlesuit.tags
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user