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