hensei-web/components/FilterModal/index.tsx
Justin Edmund 401506dcf8 Swap to using selects for some boolean fields
Charge Attack, Full Auto, and Auto Guard are not boolean values since the user can select (and the default should be) to show both on and off values. We swap to using a SelectTableField here to represent this difference.

We also added logic for Full Auto and Auto Guard fields since they are tied together in some cases (you can't show Auto Guard teams that have Full Auto disabled)
2023-03-21 19:33:22 -07:00

450 lines
13 KiB
TypeScript

import React, { useEffect, useState } from 'react'
import { getCookie, setCookie } from 'cookies-next'
import { useRouter } from 'next/router'
import { useTranslation } from 'react-i18next'
import {
Dialog,
DialogTrigger,
DialogClose,
DialogTitle,
} from '~components/Dialog'
import DialogContent from '~components/DialogContent'
import Button from '~components/Button'
import InputTableField from '~components/InputTableField'
import SelectTableField from '~components/SelectTableField'
import SliderTableField from '~components/SliderTableField'
import SwitchTableField from '~components/SwitchTableField'
import SelectItem from '~components/SelectItem'
import type { DialogProps } from '@radix-ui/react-dialog'
import CrossIcon from '~public/icons/Cross.svg'
import './index.scss'
interface Props extends DialogProps {
defaultFilterSet: FilterSet
filterSet: FilterSet
}
const MAX_CHARACTERS = 5
const MAX_WEAPONS = 13
const MAX_SUMMONS = 8
const FilterModal = (props: Props) => {
// Set up router
const router = useRouter()
const locale = router.locale
// Set up translation
const { t } = useTranslation('common')
// Refs
const headerRef = React.createRef<HTMLDivElement>()
const footerRef = React.createRef<HTMLDivElement>()
// States
const [open, setOpen] = useState(false)
const [chargeAttackOpen, setChargeAttackOpen] = useState(false)
const [fullAutoOpen, setFullAutoOpen] = useState(false)
const [autoGuardOpen, setAutoGuardOpen] = useState(false)
const [minCharacterCount, setMinCharacterCount] = useState(3)
const [minWeaponCount, setMinWeaponCount] = useState(5)
const [minSummonCount, setMinSummonCount] = useState(2)
const [maxButtonsCount, setMaxButtonsCount] = useState(0)
const [maxTurnsCount, setMaxTurnsCount] = useState(0)
// Filter states
const [fullAuto, setFullAuto] = useState(props.defaultFilterSet.full_auto)
const [autoGuard, setAutoGuard] = useState(props.defaultFilterSet.auto_guard)
const [chargeAttack, setChargeAttack] = useState(
props.filterSet.charge_attack
)
const [minCharacterCount, setMinCharacterCount] = useState(
props.filterSet.characters_count
)
const [minWeaponCount, setMinWeaponCount] = useState(
props.filterSet.weapons_count
)
const [minSummonCount, setMinSummonCount] = useState(
props.filterSet.summons_count
)
const [minSummonCount, setMinSummonCount] = useState(DEFAULT_MIN_SUMMONS)
props.defaultFilterSet.summons_count
const [maxButtonsCount, setMaxButtonsCount] = useState(
props.filterSet.button_count
)
const [maxTurnsCount, setMaxTurnsCount] = useState(props.filterSet.turn_count)
const [userQuality, setUserQuality] = useState(props.filterSet.user_quality)
props.defaultFilterSet.user_quality
)
const [nameQuality, setNameQuality] = useState(props.filterSet.name_quality)
props.defaultFilterSet.name_quality
)
const [originalOnly, setOriginalOnly] = useState(props.filterSet.original)
props.defaultFilterSet.original
)
// Hooks
useEffect(() => {
if (props.open !== undefined) setOpen(props.open)
})
useEffect(() => {
setFilterSet(props.filterSet)
}, [props.filterSet])
useEffect(() => {
setFullAuto(filterSet.full_auto)
setAutoGuard(filterSet.auto_guard)
setChargeAttack(filterSet.charge_attack)
setMinCharacterCount(filterSet.characters_count)
setMinSummonCount(filterSet.summons_count)
setMinWeaponCount(filterSet.weapons_count)
setMaxButtonsCount(filterSet.button_count)
setMaxTurnsCount(filterSet.turn_count)
setNameQuality(filterSet.name_quality)
setUserQuality(filterSet.user_quality)
setOriginalOnly(filterSet.original)
}, [filterSet])
function sendFilters() {
setChargeAttackOpen(name === 'charge_attack' ? !chargeAttackOpen : false)
setFullAutoOpen(name === 'full_auto' ? !fullAutoOpen : false)
setAutoGuardOpen(name === 'auto_guard' ? !autoGuardOpen : false)
}
function saveFilters() {
const filters = {
full_auto: fullAuto,
auto_guard: autoGuard,
charge_attack: chargeAttack,
characters_count: minCharacterCount,
weapons_count: minWeaponCount,
summons_count: minSummonCount,
button_count: maxButtonsCount,
turn_count: maxTurnsCount,
name_quality: nameQuality,
user_quality: userQuality,
original: originalOnly,
}
console.log(filters)
function openSelect(name: 'charge_attack' | 'full_auto' | 'auto_guard') {
setChargeAttackOpen(name === 'charge_attack' ? !chargeAttackOpen : false)
setFullAutoOpen(name === 'full_auto' ? !fullAutoOpen : false)
setAutoGuardOpen(name === 'auto_guard' ? !autoGuardOpen : false)
}
setCookie('filters', filters, { path: '/' })
props.sendAdvancedFilters(filters)
// openChange()
}
function resetFilters() {
setFullAuto(props.defaultFilterSet.full_auto)
setAutoGuard(props.defaultFilterSet.auto_guard)
setChargeAttack(props.defaultFilterSet.charge_attack)
setMinCharacterCount(props.defaultFilterSet.characters_count)
setMinWeaponCount(props.defaultFilterSet.weapons_count)
setMinSummonCount(props.defaultFilterSet.summons_count)
setMaxButtonsCount(props.defaultFilterSet.button_count)
setMaxTurnsCount(props.defaultFilterSet.turn_count)
setUserQuality(props.defaultFilterSet.user_quality)
setNameQuality(props.defaultFilterSet.name_quality)
setOriginalOnly(props.defaultFilterSet.original)
}
function openChange() {
if (open) {
setOpen(false)
if (props.onOpenChange) props.onOpenChange(false)
} else {
setOpen(true)
if (props.onOpenChange) props.onOpenChange(true)
}
}
function onEscapeKeyDown(event: KeyboardEvent) {
event.preventDefault()
openChange()
}
function onOpenAutoFocus(event: Event) {
event.preventDefault()
}
// Value listeners
function handleChargeAttackValueChange(value: string) {
setChargeAttack(parseInt(value))
}
function handleFullAutoValueChange(value: string) {
const newValue = parseInt(value)
setFullAuto(newValue)
if (newValue === 0 || (newValue === -1 && autoGuard === 1))
setAutoGuard(newValue)
}
function handleAutoGuardValueChange(value: string) {
const newValue = parseInt(value)
setAutoGuard(newValue)
if (newValue === 1 || (newValue === -1 && fullAuto === 0))
setFullAuto(newValue)
}
function handleMinCharactersValueChange(value: number) {
setMinCharacterCount(value)
}
function handleMinSummonsValueChange(value: number) {
setMinSummonCount(value)
}
function handleMinWeaponsValueChange(value: number) {
setMinWeaponCount(value)
}
function handleMaxButtonsCountValueChange(value: number) {
setMaxButtonsCount(value)
}
function handleMaxTurnsCountValueChange(value: number) {
setMaxTurnsCount(value)
}
function handleNameQualityValueChange(value: boolean) {
setNameQuality(value)
}
function handleUserQualityValueChange(value: boolean) {
setUserQuality(value)
}
function handleOriginalOnlyValueChange(value: boolean) {
setOriginalOnly(value)
}
// Sliders
const minCharactersField = () => (
<SliderTableField
name="min_characters"
description={t('modals.filters.descriptions.min_characters')}
label={t('modals.filters.labels.min_characters')}
value={minCharacterCount}
min={0}
max={MAX_CHARACTERS}
step={1}
onValueChange={handleMinCharactersValueChange}
/>
)
const minWeaponsField = () => (
<SliderTableField
name="min_weapons"
description={t('modals.filters.descriptions.min_weapons')}
label={t('modals.filters.labels.min_weapons')}
value={minWeaponCount}
min={0}
max={MAX_WEAPONS}
step={1}
onValueChange={handleMinWeaponsValueChange}
/>
)
const minSummonsField = () => (
<SliderTableField
name="min_summons"
description={t('modals.filters.descriptions.min_summons')}
label={t('modals.filters.labels.min_summons')}
value={minSummonCount}
min={0}
max={MAX_SUMMONS}
step={1}
onValueChange={handleMinSummonsValueChange}
/>
)
// Selects
const fullAutoField = () => (
<SelectTableField
name="full_auto"
label={t('modals.filters.labels.full_auto')}
open={fullAutoOpen}
value={`${fullAuto}`}
onOpenChange={() => openSelect('full_auto')}
onClose={() => setFullAutoOpen(false)}
onChange={handleFullAutoValueChange}
>
<SelectItem key="on-off" value="-1">
{t('modals.filters.options.on_and_off')}
</SelectItem>
<SelectItem key="on" value="1">
{t('modals.filters.options.on')}
</SelectItem>
<SelectItem key="off" value="0">
{t('modals.filters.options.off')}
</SelectItem>
</SelectTableField>
)
const autoGuardField = () => (
<SelectTableField
name="auto_guard"
label={t('modals.filters.labels.auto_guard')}
open={autoGuardOpen}
value={`${autoGuard}`}
onOpenChange={() => openSelect('auto_guard')}
onClose={() => setAutoGuardOpen(false)}
onChange={handleAutoGuardValueChange}
>
<SelectItem key="on-off" value="-1">
{t('modals.filters.options.on_and_off')}
</SelectItem>
<SelectItem key="on" value="1">
{t('modals.filters.options.on')}
</SelectItem>
<SelectItem key="off" value="0">
{t('modals.filters.options.off')}
</SelectItem>
</SelectTableField>
)
const chargeAttackField = () => (
<SelectTableField
name="charge_attack"
label={t('modals.filters.labels.charge_attack')}
open={chargeAttackOpen}
value={`${chargeAttack}`}
onOpenChange={() => openSelect('charge_attack')}
onClose={() => setChargeAttackOpen(false)}
onChange={handleChargeAttackValueChange}
>
<SelectItem key="on-off" value="-1">
{t('modals.filters.options.on_and_off')}
</SelectItem>
<SelectItem key="on" value="1">
{t('modals.filters.options.on')}
</SelectItem>
<SelectItem key="off" value="0">
{t('modals.filters.options.off')}
</SelectItem>
</SelectTableField>
)
// Switches
const nameQualityField = () => (
<SwitchTableField
name="name_quality"
label={t('modals.filters.labels.name_quality')}
value={nameQuality}
onValueChange={handleNameQualityValueChange}
/>
)
const userQualityField = () => (
<SwitchTableField
name="user_quality"
label={t('modals.filters.labels.user_quality')}
value={userQuality}
onValueChange={handleUserQualityValueChange}
/>
)
const originalOnlyField = () => (
<SwitchTableField
name="original_only"
label={t('modals.filters.labels.original_only')}
value={originalOnly}
onValueChange={handleOriginalOnlyValueChange}
/>
)
// Inputs
const maxButtonsField = () => (
<InputTableField
name="min_characters"
description={t('modals.filters.descriptions.max_buttons')}
placeholder="0"
label={t('modals.filters.labels.max_buttons')}
value={maxButtonsCount}
onValueChange={handleMaxButtonsCountValueChange}
/>
)
const maxTurnsField = () => (
<InputTableField
name="min_turns"
description={t('modals.filters.descriptions.max_turns')}
placeholder="0"
label={t('modals.filters.labels.max_turns')}
value={maxTurnsCount}
onValueChange={handleMaxTurnsCountValueChange}
/>
)
return (
<Dialog open={open} onOpenChange={openChange}>
<DialogTrigger asChild>{props.children}</DialogTrigger>
<DialogContent
className="Filter"
headerref={headerRef}
footerref={footerRef}
onEscapeKeyDown={onEscapeKeyDown}
onOpenAutoFocus={onOpenAutoFocus}
>
<div className="DialogHeader" ref={headerRef}>
<div className="DialogTop">
<DialogTitle className="DialogTitle">
{t('modals.filters.title')}
</DialogTitle>
</div>
<DialogClose className="DialogClose" asChild>
<span>
<CrossIcon />
</span>
</DialogClose>
</div>
<div className="Fields">
{chargeAttackField()}
{fullAutoField()}
{autoGuardField()}
{maxButtonsField()}
{maxTurnsField()}
{minCharactersField()}
{minSummonsField()}
{minWeaponsField()}
{nameQualityField()}
{userQualityField()}
{originalOnlyField()}
</div>
<div className="DialogFooter" ref={footerRef}>
<div className="Buttons Spaced">
<Button
blended={true}
text={t('modals.filters.buttons.clear')}
onClick={resetFilters}
/>
<Button
contained={true}
text={t('modals.filters.buttons.confirm')}
onClick={saveFilters}
/>
</div>
</div>
</DialogContent>
</Dialog>
)
}
export default FilterModal