add CharacterTag component, show tags in db list

This commit is contained in:
Justin Edmund 2025-12-15 17:48:29 -08:00
parent d1376396b4
commit cbef8f5518
3 changed files with 251 additions and 6 deletions

View file

@ -0,0 +1,67 @@
<svelte:options runes={true} />
<script lang="ts">
import type { Cell } from 'wx-svelte-grid'
import type { Character } from '$lib/types/api/entities'
import CharacterTag from '$lib/components/tags/CharacterTag.svelte'
const { row }: Cell = $props()
// Cast row to Character type for type safety
const character = row as Character
// Get display name
const displayName = $derived.by(() => {
const nameObj = character.name
if (!nameObj) return '—'
if (typeof nameObj === 'string') return nameObj
return nameObj.en || nameObj.ja || '—'
})
// Check if character has season (seasonal variant)
const hasSeason = $derived(
character.season !== undefined && character.season !== null && character.season > 0
)
// Check if character has series (need to check for non-empty array)
const hasSeries = $derived.by(() => {
if (!character.series || !Array.isArray(character.series)) return false
return character.series.length > 0
})
</script>
<div class="name-cell">
<span class="name">{displayName}</span>
{#if hasSeason || hasSeries}
<div class="tags">
{#if hasSeason}
<CharacterTag {character} type="season" />
{/if}
{#if hasSeries}
<CharacterTag {character} type="series" />
{/if}
</div>
{/if}
</div>
<style lang="scss">
@use '$src/themes/spacing' as *;
@use '$src/themes/typography' as *;
.name-cell {
display: flex;
flex-direction: column;
gap: $unit-half;
padding: $unit-half 0;
}
.name {
font-weight: $medium;
}
.tags {
display: flex;
flex-wrap: wrap;
gap: $unit-half;
}
</style>

View file

@ -0,0 +1,182 @@
<svelte:options runes={true} />
<script lang="ts">
import type { Character } from '$lib/types/api/entities'
import type { CharacterSeriesRef } from '$lib/types/api/characterSeries'
import { getElementLabel } from '$lib/utils/element'
import { CHARACTER_SEASON_NAMES, CHARACTER_SERIES_NAMES } from '$lib/types/enums'
type TagType = 'element' | 'season' | 'series'
interface Props {
/** The character to display the tag for */
character: Character
/** Which characteristic to display */
type: TagType
/** Optional specific series index to display (for multi-series characters) */
seriesIndex?: number
}
let { character, type, seriesIndex = 0 }: Props = $props()
// Get element class name for styling
const elementClass = $derived.by(() => {
const element = character.element
switch (element) {
case 1:
return 'wind'
case 2:
return 'fire'
case 3:
return 'water'
case 4:
return 'earth'
case 5:
return 'dark'
case 6:
return 'light'
default:
return 'neutral'
}
})
// Get the display text based on tag type
const displayText = $derived.by(() => {
switch (type) {
case 'element':
return getElementLabel(character.element)
case 'season':
if (character.season === undefined || character.season === null) return null
return CHARACTER_SEASON_NAMES[character.season] ?? null
case 'series':
// Handle array of CharacterSeriesRef objects
if (character.series && Array.isArray(character.series)) {
const seriesArray = character.series as (number | CharacterSeriesRef)[]
const seriesValue = seriesArray[seriesIndex]
if (seriesValue === undefined) return null
// Check if it's an object (CharacterSeriesRef) or number
if (typeof seriesValue === 'object' && seriesValue !== null && 'name' in seriesValue) {
return seriesValue.name.en
}
// Legacy number format
if (typeof seriesValue === 'number') {
return CHARACTER_SERIES_NAMES[seriesValue] ?? null
}
}
// Fallback to seriesNames if available
if (character.seriesNames && character.seriesNames[seriesIndex]) {
return character.seriesNames[seriesIndex]
}
return null
default:
return null
}
})
// Only render if we have text to display
const shouldRender = $derived(displayText !== null && displayText !== '—')
</script>
{#if shouldRender}
<span class="character-tag {elementClass}">
{displayText}
</span>
{/if}
<style lang="scss">
@use '$src/themes/colors' as *;
@use '$src/themes/spacing' as *;
@use '$src/themes/typography' as *;
@use '$src/themes/layout' as *;
.character-tag {
display: inline-flex;
align-items: center;
justify-content: center;
padding: $unit-fourth $unit;
border-radius: $item-corner-small;
font-size: $font-tiny;
font-weight: $medium;
white-space: nowrap;
line-height: 1;
// Element-specific styling
&.wind {
background-color: $wind-bg-20;
color: $wind-text-20;
}
&.fire {
background-color: $fire-bg-20;
color: $fire-text-20;
}
&.water {
background-color: $water-bg-20;
color: $water-text-20;
}
&.earth {
background-color: $earth-bg-20;
color: $earth-text-20;
}
&.dark {
background-color: $dark-bg-20;
color: $dark-text-20;
}
&.light {
background-color: $light-bg-20;
color: $light-text-20;
}
&.neutral {
background-color: $grey-80;
color: $grey-40;
}
}
// Dark mode adjustments
:global(.dark) .character-tag {
&.wind {
background-color: $wind-text-10;
color: $wind-bg-20;
}
&.fire {
background-color: $fire-text-10;
color: $fire-bg-20;
}
&.water {
background-color: $water-text-10;
color: $water-bg-20;
}
&.earth {
background-color: $earth-text-10;
color: $earth-bg-20;
}
&.dark {
background-color: $dark-text-10;
color: $dark-bg-20;
}
&.light {
background-color: $light-text-10;
color: $light-bg-20;
}
&.neutral {
background-color: $grey-30;
color: $grey-80;
}
}
</style>

View file

@ -6,6 +6,7 @@
// Svelte components
import CharacterImageCell from '$lib/components/database/cells/CharacterImageCell.svelte'
import CharacterNameCell from '$lib/components/database/cells/CharacterNameCell.svelte'
import CharacterUncapCell from '$lib/components/database/cells/CharacterUncapCell.svelte'
import DatabaseGridWithProvider from '$lib/components/database/DatabaseGridWithProvider.svelte'
import ElementCell from '$lib/components/database/cells/ElementCell.svelte'
@ -28,12 +29,7 @@
header: 'Name',
flexgrow: 1,
sort: true,
template: (nameObj: { en: any; ja: any }) => {
if (!nameObj) return '—'
if (typeof nameObj === 'string') return nameObj
// Handle {en: "...", ja: "..."} structure
return nameObj.en || nameObj.ja || '—'
}
cell: CharacterNameCell
},
{
id: 'rarity',