Implement filter by features for battlesuit list

This commit is contained in:
Sakura Knoll
2021-12-30 17:17:10 +09:00 Unverified
parent f3a2347d48
commit 1bb545c534
13 changed files with 252 additions and 72 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Drive Kometa", "name": "Drive Kometa",
"type": "psychic", "type": "psychic",
"valkyrie": "bronya", "valkyrie": "bronya",
"strengths": [], "strengths": ["physical", "gather", "weaken", "burst", "time mastery"],
"leader": { "leader": {
"core": { "core": {
"name": "Mental Sync", "name": "Mental Sync",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Danzai Spectramancer", "name": "Danzai Spectramancer",
"type": "mecha", "type": "mecha",
"valkyrie": "mei", "valkyrie": "mei",
"strengths": [], "strengths": ["physical"],
"leader": { "leader": {
"core": { "core": {
"name": "Phantom Outbreak", "name": "Phantom Outbreak",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Fervent Tempo Δ", "name": "Fervent Tempo Δ",
"type": "psychic", "type": "psychic",
"valkyrie": "olenyevas", "valkyrie": "olenyevas",
"strengths": [], "strengths": ["physical"],
"leader": { "leader": {
"core": { "core": {
"name": "SHOWTIME!", "name": "SHOWTIME!",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Hawk of the Fog", "name": "Hawk of the Fog",
"type": "psychic", "type": "psychic",
"valkyrie": "fuhua", "valkyrie": "fuhua",
"strengths": [], "strengths": ["lightning dmg", "heavy atk", "paralyze", "time mastery"],
"leader": { "leader": {
"core": { "core": {
"name": "Chelonic Unity", "name": "Chelonic Unity",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Herrscher of Flamescion", "name": "Herrscher of Flamescion",
"type": "psychic", "type": "psychic",
"valkyrie": "kiana", "valkyrie": "kiana",
"strengths": ["fire dmg", "heal", "time mastery", "gather"], "strengths": ["fire dmg", "heal", "time mastery", "gather", "aerial"],
"leader": { "leader": {
"core": { "core": {
"name": "Nameless Knight-Errant's Path", "name": "Nameless Knight-Errant's Path",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Herrscher of the Void", "name": "Herrscher of the Void",
"type": "biologic", "type": "biologic",
"valkyrie": "kiana", "valkyrie": "kiana",
"strengths": ["physical", "time mastery", "fast atk"], "strengths": ["physical", "time mastery", "fast atk", "aerial"],
"leader": { "leader": {
"core": { "core": {
"name": "Void Throne", "name": "Void Throne",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Infinite Ouroboros", "name": "Infinite Ouroboros",
"type": "mecha", "type": "mecha",
"valkyrie": "mobius", "valkyrie": "mobius",
"strengths": ["lightning dmg", "time mastery"], "strengths": ["lightning dmg", "time mastery", "aerial"],
"leader": { "leader": {
"core": { "core": {
"name": "Infinite Fate", "name": "Infinite Fate",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Miss Pink Elf♪", "name": "Miss Pink Elf♪",
"type": "psychic", "type": "psychic",
"valkyrie": "elysia", "valkyrie": "elysia",
"strengths": ["physical", "time mastery", "fast atk"], "strengths": ["physical", "time mastery", "fast atk", "aerial"],
"leader": { "leader": {
"core": { "core": {
"name": "An Ode to Love and Compassion", "name": "An Ode to Love and Compassion",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Prinzessin der Verurteilung!", "name": "Prinzessin der Verurteilung!",
"type": "biologic", "type": "biologic",
"valkyrie": "fischl", "valkyrie": "fischl",
"strengths": ["lightning dmg", "time mastery", "fast atk"], "strengths": ["lightning dmg", "time mastery", "fast atk", "aerial"],
"leader": { "leader": {
"core": { "core": {
"name": "Evernight Suite", "name": "Evernight Suite",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Sweet 'n' Spicy", "name": "Sweet 'n' Spicy",
"type": "mecha", "type": "mecha",
"valkyrie": "carole", "valkyrie": "carole",
"strengths": ["physical", "time mastery", "stun", "gather"], "strengths": ["physical", "time mastery", "stun", "gather", "aerial"],
"leader": { "leader": {
"core": { "core": {
"name": "Star of Hope", "name": "Star of Hope",
+1 -1
View File
@@ -4,7 +4,7 @@
"name": "Striker Fulminata", "name": "Striker Fulminata",
"type": "biologic", "type": "biologic",
"valkyrie": "mei", "valkyrie": "mei",
"strengths": [], "strengths": ["physical", "burst", "fast atk", "paralyze"],
"leader": { "leader": {
"core": { "core": {
"name": "Sakura Aura", "name": "Sakura Aura",
+21
View File
@@ -86,6 +86,27 @@ export const theme: Theme = {
lineHeight: 'heading', lineHeight: 'heading',
}, },
}, },
buttons: {
primary: {
color: 'primary',
borderColor: 'primary',
borderStyle: 'solid',
borderWidth: 1,
bg: 'background',
'&:hover, &.active': {
color: 'background',
bg: 'primary',
},
},
secondary: {
color: 'background',
bg: 'secondary',
},
gray: {
color: 'background',
bg: 'gray',
},
},
styles: { styles: {
root: { root: {
fontFamily: 'body', fontFamily: 'body',
+220 -61
View File
@@ -1,8 +1,9 @@
import { Box, Heading, Flex, Text, Link } from '@theme-ui/components' import { Box, Heading, Flex, Text, Link, Button } from '@theme-ui/components'
import Image from 'next/image' import Image from 'next/image'
import NextLink from 'next/link' import NextLink from 'next/link'
import { pick } from 'ramda' import { pick } from 'ramda'
import React from 'react' import React, { useCallback, useMemo, useState } from 'react'
import SquareImageBox from '../../../components/atoms/SquareImageBox'
import Breadcrumb from '../../../components/organisms/Breadcrumb' import Breadcrumb from '../../../components/organisms/Breadcrumb'
import Honkai3rdNavigator from '../../../components/organisms/Honkai3rdNavigator' import Honkai3rdNavigator from '../../../components/organisms/Honkai3rdNavigator'
import { import {
@@ -10,11 +11,133 @@ import {
listBattlesuits, listBattlesuits,
} from '../../../data/honkai3rd/battlesuits' } from '../../../data/honkai3rd/battlesuits'
type BattlesuitListItemData = Pick<BattlesuitData, 'id' | 'name' | 'strengths'>
interface BattlesuitListPageProps { interface BattlesuitListPageProps {
battlesuits: Pick<BattlesuitData, 'id' | 'name'>[] battlesuits: BattlesuitListItemData[]
} }
const featureFilterOptions = [
{ value: 'all', label: 'All' },
{ value: 'physical', label: 'Physical' },
{ value: 'fire dmg', label: 'Fire DMG' },
{ value: 'ice dmg', label: 'Ice DMG' },
{ value: 'lightning dmg', label: 'Lightning DMG' },
{ value: 'freeze', label: 'Freeze' },
{ value: 'paralyze', label: 'Paralyze' },
{ value: 'stun', label: 'Stun' },
{ value: 'ignite', label: 'Ignite' },
{ value: 'bleed', label: 'Bleed' },
{ value: 'heavy atk', label: 'Heavy ATK' },
{ value: 'weaken', label: 'Weaken' },
{ value: 'impair', label: 'Impair' },
{ value: 'time mastery', label: 'Time Mastery' },
{ value: 'gather', label: 'Gather' },
{ value: 'heal', label: 'Heal' },
{ value: 'fast atk', label: 'Fast ATK' },
{ value: 'burst', label: 'Burst' },
{ value: 'aerial', label: 'Aerial' },
]
const BattlesuitListPage = ({ battlesuits }: BattlesuitListPageProps) => { const BattlesuitListPage = ({ battlesuits }: BattlesuitListPageProps) => {
const [filter, setFilter] = useState('all')
const isBattlesuitHidden = useCallback(
(battlesuit: BattlesuitListItemData) => {
switch (filter) {
case 'physical':
case 'fire dmg':
case 'ice dmg':
case 'lightning dmg':
case 'freeze':
case 'paralyze':
case 'stun':
case 'ignite':
case 'bleed':
case 'heavy atk':
case 'weaken':
case 'impair':
case 'time mastery':
case 'gather':
case 'heal':
case 'fast atk':
case 'burst':
case 'aerial':
return !battlesuit.strengths.some((strength) => strength === filter)
default:
case 'all':
return false
}
},
[filter]
)
const battlesuitList = useMemo(() => {
return battlesuits.map((battlesuit) => {
const hidden = isBattlesuitHidden(battlesuit)
return (
<Box
key={battlesuit.id}
className={hidden ? 'hidden' : ''}
sx={{
width: '160px',
padding: 2,
margin: 1,
borderColor: 'gray.3',
borderWidth: 1,
borderStyle: 'solid',
borderRadius: 8,
transition:
'box-shadow 200ms ease-in-out, opacity 200ms ease-in-out',
'&:hover': {
borderColor: 'gray.3',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
},
'&.hidden': {
display: 'none',
},
}}
>
<NextLink
href={`/honkai3rd/battlesuits/${battlesuit.id}`}
key={battlesuit.id}
passHref={true}
>
<Link>
<Box
sx={{
position: 'relative',
overflow: 'hidden',
width: '140px',
height: '140px',
borderRadius: 4,
}}
>
<Image
alt={battlesuit.name}
layout='fill'
objectFit='cover'
src={`/assets/honkai3rd/battlesuits/portrait-${battlesuit.id}.png`}
/>
</Box>
<Box
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
width: '100%',
whiteSpace: 'nowrap',
textAlign: 'center',
}}
>
<Text>{battlesuit.name}</Text>
</Box>
</Link>
</NextLink>
</Box>
)
})
}, [battlesuits, isBattlesuitHidden])
return ( return (
<Box> <Box>
<Honkai3rdNavigator /> <Honkai3rdNavigator />
@@ -28,6 +151,60 @@ const BattlesuitListPage = ({ battlesuits }: BattlesuitListPageProps) => {
/> />
<Heading as='h1'>Battlesuits</Heading> <Heading as='h1'>Battlesuits</Heading>
<Box>
<Heading as='h3'>Filter by Features</Heading>
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
{featureFilterOptions.map(({ value, label }) => {
return (
<FilterButton
active={value === filter}
setFilter={setFilter}
value={value}
label={label}
/>
)
})}
</Flex>
<Heading as='h3'>Filter by Valkyries</Heading>
<Flex mb={2} sx={{ flexWrap: 'wrap' }}>
<FilterButton setFilter={setFilter} value='all' label='ALL' />
<FilterButton setFilter={setFilter} value='mecha' label='MECHA' />
<FilterButton setFilter={setFilter} value='bio' label='BIO' />
<FilterButton setFilter={setFilter} value='psy' label='PSY' />
<FilterButton setFilter={setFilter} value='qua' label='QUA' />
<FilterButton setFilter={setFilter} value='img' label='IMG' />
<FilterButton setFilter={setFilter} value='kiana' label='Kiana' />
<FilterButton setFilter={setFilter} value='mei' label='Mei' />
<FilterButton setFilter={setFilter} value='bronya' label='Bronya' />
<FilterButton setFilter={setFilter} value='himeko' label='Himeko' />
<FilterButton
setFilter={setFilter}
value='theresa'
label='Theresa'
/>
<FilterButton setFilter={setFilter} value='fu hua' label='Fu Hua' />
<FilterButton setFilter={setFilter} value='rita' label='Rita' />
<FilterButton setFilter={setFilter} value='sakura' label='Sakura' />
<FilterButton setFilter={setFilter} value='kallen' label='Kallen' />
<FilterButton
setFilter={setFilter}
value='olenyevas'
label='Olenyevas'
/>
<FilterButton setFilter={setFilter} value='seele' label='Seele' />
<FilterButton
setFilter={setFilter}
value='durandal'
label='Durandal'
/>
<FilterButton setFilter={setFilter} value='fischl' label='Fischl' />
<FilterButton setFilter={setFilter} value='elysia' label='Elysia' />
<FilterButton setFilter={setFilter} value='mobius' label='Mobius' />
<FilterButton setFilter={setFilter} value='raven' label='Raven' />
<FilterButton setFilter={setFilter} value='carole' label='Carole' />
</Flex>
</Box>
<Flex <Flex
sx={{ sx={{
@@ -35,63 +212,7 @@ const BattlesuitListPage = ({ battlesuits }: BattlesuitListPageProps) => {
justifyContent: 'space-around', justifyContent: 'space-around',
}} }}
> >
{battlesuits.map((battlesuit) => { {battlesuitList}
return (
<Box
key={battlesuit.id}
sx={{
width: '160px',
padding: 2,
margin: 1,
borderColor: 'gray.3',
borderWidth: 1,
borderStyle: 'solid',
borderRadius: 8,
'&:hover': {
borderColor: 'gray.3',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
transition: 'box-shadow 200ms ease-in-out',
},
}}
>
<NextLink
href={`/honkai3rd/battlesuits/${battlesuit.id}`}
key={battlesuit.id}
passHref={true}
>
<Link>
<Box
sx={{
position: 'relative',
overflow: 'hidden',
width: '140px',
height: '140px',
borderRadius: 4,
}}
>
<Image
alt={battlesuit.name}
layout='fill'
objectFit='cover'
src={`/assets/honkai3rd/battlesuits/portrait-${battlesuit.id}.png`}
/>
</Box>
<Box
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
width: '100%',
whiteSpace: 'nowrap',
textAlign: 'center',
}}
>
<Text>{battlesuit.name}</Text>
</Box>
</Link>
</NextLink>
</Box>
)
})}
</Flex> </Flex>
</Box> </Box>
</Box> </Box>
@@ -104,8 +225,46 @@ export async function getStaticProps() {
return { return {
props: { props: {
battlesuits: listBattlesuits().map((battlesuit) => battlesuits: listBattlesuits().map((battlesuit) =>
pick(['id', 'name'], battlesuit) pick(['id', 'name', 'strengths'], battlesuit)
), ),
}, },
} }
} }
interface FilterButtonProps {
active?: boolean
icon?: string
label: string
setFilter: (filter: string) => void
value: string
}
const FilterButton = ({
active = false,
setFilter,
icon,
label,
value,
}: FilterButtonProps) => {
const selectFilter = useCallback(() => {
setFilter(value)
}, [setFilter, value])
return (
<Button
m={1}
py={1}
px={2}
onClick={selectFilter}
className={active ? 'active' : ''}
>
{icon != null && (
<SquareImageBox
src={`/assets/honkai3rd/icons/${icon}.png`}
alt={label}
size={30}
/>
)}
{label}
</Button>
)
}