combine settings and performance into BattleTile
This commit is contained in:
parent
28b5f3de4f
commit
5d00c1580d
3 changed files with 192 additions and 172 deletions
192
src/lib/components/party/info/BattleTile.svelte
Normal file
192
src/lib/components/party/info/BattleTile.svelte
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<script lang="ts">
|
||||
import InfoTile from './InfoTile.svelte'
|
||||
import Tooltip from '$lib/components/ui/Tooltip.svelte'
|
||||
|
||||
interface Props {
|
||||
// Settings
|
||||
fullAuto?: boolean
|
||||
autoGuard?: boolean
|
||||
autoSummon?: boolean
|
||||
chargeAttack?: boolean
|
||||
// Performance
|
||||
clearTime?: number | null
|
||||
buttonCount?: number | null
|
||||
chainCount?: number | null
|
||||
summonCount?: number | null
|
||||
// Tile props
|
||||
clickable?: boolean
|
||||
onclick?: () => void
|
||||
}
|
||||
|
||||
let {
|
||||
fullAuto,
|
||||
autoGuard,
|
||||
autoSummon,
|
||||
chargeAttack,
|
||||
clearTime,
|
||||
buttonCount,
|
||||
chainCount,
|
||||
summonCount,
|
||||
clickable = false,
|
||||
onclick
|
||||
}: Props = $props()
|
||||
|
||||
// Settings tokens
|
||||
interface Setting {
|
||||
key: string
|
||||
label: string
|
||||
tooltip: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
const settings: Setting[] = $derived([
|
||||
{ key: 'chargeAttack', label: `CA ${chargeAttack ?? true ? 'On' : 'Off'}`, tooltip: 'Charge Attack', active: chargeAttack ?? true },
|
||||
{ key: 'fullAuto', label: `FA ${fullAuto ? 'On' : 'Off'}`, tooltip: 'Full Auto', active: fullAuto ?? false },
|
||||
{ key: 'autoSummon', label: `AS ${autoSummon ? 'On' : 'Off'}`, tooltip: 'Auto Summon', active: autoSummon ?? false },
|
||||
{ key: 'autoGuard', label: `AG ${autoGuard ? 'On' : 'Off'}`, tooltip: 'Auto Guard', active: autoGuard ?? false }
|
||||
])
|
||||
|
||||
// Performance display - only show non-null values
|
||||
function formatClearTime(seconds?: number | null): string | null {
|
||||
if (seconds == null || seconds <= 0) return null
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const secs = seconds % 60
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
const formattedClearTime = $derived(formatClearTime(clearTime))
|
||||
|
||||
// Build BCS string with only non-null values
|
||||
const bcsItems = $derived(() => {
|
||||
const items: string[] = []
|
||||
if (buttonCount != null) items.push(`${buttonCount}B`)
|
||||
if (chainCount != null) items.push(`${chainCount}C`)
|
||||
if (summonCount != null) items.push(`${summonCount}S`)
|
||||
return items
|
||||
})
|
||||
|
||||
const hasPerformanceData = $derived(formattedClearTime || bcsItems().length > 0)
|
||||
</script>
|
||||
|
||||
<InfoTile label="Battle" class="battle-tile" {clickable} {onclick}>
|
||||
<div class="battle-content">
|
||||
<!-- Settings tokens -->
|
||||
<div class="settings-tokens">
|
||||
{#each settings as setting (setting.key)}
|
||||
<Tooltip content={setting.tooltip}>
|
||||
<span class="token {setting.key}" class:on={setting.active} class:off={!setting.active}>
|
||||
{setting.label}
|
||||
</span>
|
||||
</Tooltip>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Performance metrics (only if any exist) -->
|
||||
{#if hasPerformanceData}
|
||||
<div class="performance">
|
||||
{#if formattedClearTime}
|
||||
<div class="clear-time">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<polyline points="12 6 12 12 16 14" />
|
||||
</svg>
|
||||
<span class="value">{formattedClearTime}</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#if bcsItems().length > 0}
|
||||
<div class="bcs-counts">
|
||||
<span class="bcs-value">{bcsItems().join(' ')}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</InfoTile>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/layout' as *;
|
||||
@use '$src/themes/typography' as *;
|
||||
|
||||
.battle-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit-2x;
|
||||
}
|
||||
|
||||
// Settings tokens
|
||||
.settings-tokens {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $unit;
|
||||
}
|
||||
|
||||
.token {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: $unit-three-quarter $unit-2x;
|
||||
border-radius: $full-corner;
|
||||
font-size: $font-small;
|
||||
font-weight: $bold;
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
background: var(--input-bg);
|
||||
|
||||
&.off {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
&.chargeAttack.on {
|
||||
background: var(--charge-attack-bg);
|
||||
color: var(--charge-attack-text);
|
||||
}
|
||||
|
||||
&.fullAuto.on,
|
||||
&.autoSummon.on {
|
||||
background: var(--full-auto-bg);
|
||||
color: var(--full-auto-text);
|
||||
}
|
||||
|
||||
&.autoGuard.on {
|
||||
background: var(--auto-guard-bg);
|
||||
color: var(--auto-guard-text);
|
||||
}
|
||||
}
|
||||
|
||||
// Performance metrics
|
||||
.performance {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $unit-2x;
|
||||
}
|
||||
|
||||
.clear-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $unit;
|
||||
|
||||
.icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: $font-medium;
|
||||
font-weight: $bold;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.bcs-counts {
|
||||
.bcs-value {
|
||||
font-size: $font-regular;
|
||||
font-weight: $medium;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--text-secondary);
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
<script lang="ts">
|
||||
import InfoTile from './InfoTile.svelte'
|
||||
|
||||
interface Props {
|
||||
clearTime?: number
|
||||
buttonCount?: number
|
||||
chainCount?: number
|
||||
summonCount?: number
|
||||
clickable?: boolean
|
||||
onclick?: () => void
|
||||
}
|
||||
|
||||
let { clearTime, buttonCount, chainCount, summonCount, clickable = false, onclick }: Props =
|
||||
$props()
|
||||
|
||||
function formatClearTime(seconds?: number): string {
|
||||
if (seconds == null || seconds <= 0) return '—'
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const secs = seconds % 60
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
function formatCount(count?: number): string {
|
||||
if (count == null) return '—'
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
const bcsDisplay = $derived(() => {
|
||||
const b = formatCount(buttonCount)
|
||||
const c = formatCount(chainCount)
|
||||
const s = formatCount(summonCount)
|
||||
return `${b}B ${c}C ${s}S`
|
||||
})
|
||||
</script>
|
||||
|
||||
<InfoTile label="Performance" class="performance-tile" {clickable} {onclick}>
|
||||
<div class="performance-content">
|
||||
<div class="clear-time">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<polyline points="12 6 12 12 16 14" />
|
||||
</svg>
|
||||
<span class="value">{formatClearTime(clearTime)}</span>
|
||||
</div>
|
||||
<div class="bcs-counts">
|
||||
<span class="bcs-value">{bcsDisplay()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</InfoTile>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/typography' as *;
|
||||
|
||||
.performance-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $unit;
|
||||
}
|
||||
|
||||
.clear-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $unit;
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: $font-large;
|
||||
font-weight: $bold;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.bcs-counts {
|
||||
.bcs-value {
|
||||
font-size: $font-medium;
|
||||
font-weight: $medium;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--text-secondary);
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
<script lang="ts">
|
||||
import InfoTile from './InfoTile.svelte'
|
||||
|
||||
interface Props {
|
||||
fullAuto?: boolean
|
||||
autoGuard?: boolean
|
||||
autoSummon?: boolean
|
||||
chargeAttack?: boolean
|
||||
clickable?: boolean
|
||||
onclick?: () => void
|
||||
}
|
||||
|
||||
let { fullAuto, autoGuard, autoSummon, chargeAttack, clickable = false, onclick }: Props =
|
||||
$props()
|
||||
|
||||
interface Setting {
|
||||
key: string
|
||||
label: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
const settings: Setting[] = $derived([
|
||||
{ key: 'chargeAttack', label: `Charge Attack ${chargeAttack ?? true ? 'On' : 'Off'}`, active: chargeAttack ?? true },
|
||||
{ key: 'fullAuto', label: `Full Auto ${fullAuto ? 'On' : 'Off'}`, active: fullAuto ?? false },
|
||||
{ key: 'autoSummon', label: `Auto Summon ${autoSummon ? 'On' : 'Off'}`, active: autoSummon ?? false },
|
||||
{ key: 'autoGuard', label: `Auto Guard ${autoGuard ? 'On' : 'Off'}`, active: autoGuard ?? false }
|
||||
])
|
||||
</script>
|
||||
|
||||
<InfoTile label="Settings" class="settings-tile" {clickable} {onclick}>
|
||||
<div class="settings-tokens">
|
||||
{#each settings as setting (setting.key)}
|
||||
<span class="token {setting.key}" class:on={setting.active} class:off={!setting.active}>
|
||||
{setting.label}
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
</InfoTile>
|
||||
|
||||
<style lang="scss">
|
||||
@use '$src/themes/spacing' as *;
|
||||
@use '$src/themes/layout' as *;
|
||||
@use '$src/themes/typography' as *;
|
||||
|
||||
.settings-tokens {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $unit;
|
||||
}
|
||||
|
||||
.token {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: $unit-three-quarter $unit-2x;
|
||||
border-radius: $full-corner;
|
||||
font-size: $font-small;
|
||||
font-weight: $bold;
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
background: var(--input-bg);
|
||||
|
||||
&.off {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
&.chargeAttack.on {
|
||||
background: var(--charge-attack-bg);
|
||||
color: var(--charge-attack-text);
|
||||
}
|
||||
|
||||
&.fullAuto.on,
|
||||
&.autoSummon.on {
|
||||
background: var(--full-auto-bg);
|
||||
color: var(--full-auto-text);
|
||||
}
|
||||
|
||||
&.autoGuard.on {
|
||||
background: var(--auto-guard-bg);
|
||||
color: var(--auto-guard-text);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in a new issue