add artifact UI components for phase 4

- DisclosureRow: iOS-style disclosure row for navigation
- ArtifactSkillRow: shows skill with modifiers and level/value controls
- ArtifactModifierList: selectable list of skills by polarity
- ArtifactGradeDisplay: shows letter grade, breakdown, recommendation
- ArtifactEditPane: main edit pane combining base props, skills, grade
This commit is contained in:
Justin Edmund 2025-12-03 16:08:17 -08:00
parent ab1243190b
commit 3a41adc4f2
5 changed files with 1082 additions and 0 deletions

View file

@ -0,0 +1,305 @@
<svelte:options runes={true} />
<script lang="ts">
import type {
ArtifactInstance,
ArtifactSkillInstance,
ArtifactSkill,
ArtifactGrade
} from '$lib/types/api/artifact'
import { isQuirkArtifact, getSkillGroupForSlot } from '$lib/types/api/artifact'
import { createQuery } from '@tanstack/svelte-query'
import { artifactQueries } from '$lib/api/queries/artifact.queries'
import { usePaneStack, type PaneConfig } from '$lib/stores/paneStack.svelte'
import DetailsSection from '$lib/components/sidebar/details/DetailsSection.svelte'
import DetailRow from '$lib/components/sidebar/details/DetailRow.svelte'
import Select from '$lib/components/ui/Select.svelte'
import ArtifactSkillRow from './ArtifactSkillRow.svelte'
import ArtifactModifierList from './ArtifactModifierList.svelte'
import ArtifactGradeDisplay from './ArtifactGradeDisplay.svelte'
import { getElementIcon } from '$lib/utils/images'
interface Props {
/** The artifact instance being edited */
artifact: ArtifactInstance
/** Handler when artifact is updated */
onUpdate?: (updates: Partial<ArtifactInstance>) => void
/** Whether editing is disabled (view-only mode) */
disabled?: boolean
}
const { artifact, onUpdate, disabled = false }: Props = $props()
// Get the pane stack from context for pushing modifier selection panes
const paneStack = usePaneStack()
// Local state for edits
let element = $state(artifact.element)
let level = $state(artifact.level)
let proficiency = $state(artifact.proficiency)
let skills = $state<(ArtifactSkillInstance | null)[]>([...artifact.skills])
// Derived values
const artifactData = $derived(artifact.artifact)
const isQuirk = $derived(isQuirkArtifact(artifactData))
const canChangeElement = $derived(true) // Artifacts can always change element
const canChangeProficiency = $derived(isQuirk) // Only quirk artifacts have variable proficiency
// Query all skills for skill rows
const skillsQuery = createQuery(() => artifactQueries.skills())
// Get skills available for each slot
function getSkillsForSlot(slot: number): ArtifactSkill[] {
if (!skillsQuery.data) return []
const group = getSkillGroupForSlot(slot)
return skillsQuery.data.filter((s) => s.skillGroup === group)
}
// Element options
const elementOptions = [
{ value: 1, label: 'Wind', image: getElementIcon(1) },
{ value: 2, label: 'Fire', image: getElementIcon(2) },
{ value: 3, label: 'Water', image: getElementIcon(3) },
{ value: 4, label: 'Earth', image: getElementIcon(4) },
{ value: 5, label: 'Dark', image: getElementIcon(5) },
{ value: 6, label: 'Light', image: getElementIcon(6) }
]
// Level options (1-5 for standard, fixed at 1 for quirk)
const levelOptions = $derived(
isQuirk
? [{ value: 1, label: '1' }]
: [
{ value: 1, label: '1' },
{ value: 2, label: '2' },
{ value: 3, label: '3' },
{ value: 4, label: '4' },
{ value: 5, label: '5' }
]
)
// Proficiency options (1-10 for quirk artifacts)
const proficiencyOptions = [
{ value: 1, label: 'Sabre' },
{ value: 2, label: 'Dagger' },
{ value: 3, label: 'Spear' },
{ value: 4, label: 'Axe' },
{ value: 5, label: 'Staff' },
{ value: 6, label: 'Gun' },
{ value: 7, label: 'Melee' },
{ value: 8, label: 'Bow' },
{ value: 9, label: 'Harp' },
{ value: 10, label: 'Katana' }
]
// Get proficiency display name
function getProficiencyName(profValue: number | null | undefined): string {
if (!profValue) return '—'
const option = proficiencyOptions.find((o) => o.value === profValue)
return option?.label ?? '—'
}
// Push modifier selection pane for a specific slot
function handleSelectModifier(slot: number) {
const config: PaneConfig = {
id: `modifier-select-${slot}`,
title: `Select Skill ${slot}`,
component: ArtifactModifierList,
props: {
slot,
selectedModifier: skills[slot - 1]?.modifier,
onSelect: (skill: ArtifactSkill) => handleModifierSelected(slot, skill)
}
}
paneStack.push(config)
}
// Handle when a modifier is selected from the list
function handleModifierSelected(slot: number, skill: ArtifactSkill) {
const index = slot - 1
const newSkills = [...skills]
// Create new skill instance with first available strength and level 1
const firstStrength = skill.baseValues.find((v) => v !== null && v !== 0) ?? 0
newSkills[index] = {
modifier: skill.modifier,
strength: firstStrength,
level: 1
}
skills = newSkills
notifyUpdate()
// Pop back to the edit pane
paneStack.pop()
}
// Handle skill updates from skill row
function handleUpdateSkill(slot: number, update: Partial<ArtifactSkillInstance>) {
const index = slot - 1
const currentSkill = skills[index]
if (!currentSkill) return
const newSkills = [...skills]
newSkills[index] = { ...currentSkill, ...update }
skills = newSkills
notifyUpdate()
}
// Handle element change
function handleElementChange(newElement: number | undefined) {
if (newElement === undefined) return
element = newElement
notifyUpdate()
}
// Handle level change
function handleLevelChange(newLevel: number | undefined) {
if (newLevel === undefined) return
level = newLevel
notifyUpdate()
}
// Handle proficiency change
function handleProficiencyChange(newProficiency: number | undefined) {
if (newProficiency === undefined) return
proficiency = newProficiency
notifyUpdate()
}
// Notify parent of updates
function notifyUpdate() {
if (!onUpdate) return
const updates: Partial<ArtifactInstance> = {
element,
level,
skills: [...skills]
}
if (isQuirk && proficiency !== undefined) {
updates.proficiency = proficiency
}
onUpdate(updates)
}
// Current grade (from artifact or could be recalculated)
const currentGrade: ArtifactGrade = $derived(artifact.grade)
</script>
<div class="artifact-edit-pane">
<DetailsSection title="Base Properties">
<DetailRow label="Element" noHover>
{#if disabled}
<span class="element-display">
<img src={getElementIcon(element)} alt="" class="element-icon" />
{elementOptions.find((o) => o.value === element)?.label ?? '—'}
</span>
{:else}
<Select
options={elementOptions}
value={element}
onValueChange={handleElementChange}
size="small"
contained
{disabled}
/>
{/if}
</DetailRow>
{#if canChangeProficiency}
<DetailRow label="Proficiency" noHover>
{#if disabled}
<span>{getProficiencyName(proficiency)}</span>
{:else}
<Select
options={proficiencyOptions}
value={proficiency}
onValueChange={handleProficiencyChange}
size="small"
contained
placeholder="Select proficiency"
{disabled}
/>
{/if}
</DetailRow>
{:else}
<DetailRow label="Proficiency" value={getProficiencyName(artifactData.proficiency)} />
{/if}
<DetailRow label="Level" noHover>
{#if disabled || isQuirk}
<span>{level}</span>
{:else}
<Select
options={levelOptions}
value={level}
onValueChange={handleLevelChange}
size="small"
contained
{disabled}
/>
{/if}
</DetailRow>
</DetailsSection>
{#if !isQuirk}
<DetailsSection title="Skills">
<div class="skills-list">
{#each [1, 2, 3, 4] as slot}
<ArtifactSkillRow
{slot}
skill={skills[slot - 1] ?? null}
availableSkills={getSkillsForSlot(slot)}
onSelectModifier={() => handleSelectModifier(slot)}
onUpdateSkill={(update) => handleUpdateSkill(slot, update)}
{disabled}
/>
{/each}
</div>
</DetailsSection>
{/if}
<DetailsSection title="Grade">
<div class="grade-section">
<ArtifactGradeDisplay grade={currentGrade} />
</div>
</DetailsSection>
</div>
<style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
@use '$src/themes/layout' as layout;
.artifact-edit-pane {
display: flex;
flex-direction: column;
gap: spacing.$unit-3x;
padding-bottom: spacing.$unit-4x;
}
.element-display {
display: flex;
align-items: center;
gap: spacing.$unit-half;
}
.element-icon {
width: 20px;
height: 20px;
}
.skills-list {
display: flex;
flex-direction: column;
gap: spacing.$unit;
padding: 0 spacing.$unit;
}
.grade-section {
padding: spacing.$unit;
}
</style>

View file

@ -0,0 +1,237 @@
<svelte:options runes={true} />
<script lang="ts">
import type { ArtifactGrade } from '$lib/types/api/artifact'
interface Props {
/** The grade to display */
grade: ArtifactGrade
/** Whether to show the full breakdown (default: true) */
showBreakdown?: boolean
/** Whether to show the recommendation (default: true) */
showRecommendation?: boolean
/** Size variant */
size?: 'small' | 'medium' | 'large'
}
const {
grade,
showBreakdown = true,
showRecommendation = true,
size = 'medium'
}: Props = $props()
// Get grade letter class for styling
const gradeClass = $derived(grade.letter?.toLowerCase() ?? 'none')
// Format score as percentage
const scoreDisplay = $derived(
grade.score !== null ? `${Math.round(grade.score)}%` : '—'
)
// Action display mapping
const actionLabels: Record<string, { label: string; class: string }> = {
keep: { label: 'Keep', class: 'action-keep' },
reroll: { label: 'Reroll', class: 'action-reroll' },
scrap: { label: 'Scrap', class: 'action-scrap' }
}
</script>
<div class="grade-display" class:size-small={size === 'small'} class:size-large={size === 'large'}>
{#if grade.letter}
<div class="grade-header">
<div class="grade-letter grade-{gradeClass}">
{grade.letter}
</div>
{#if grade.score !== null}
<div class="grade-score">{scoreDisplay}</div>
{/if}
</div>
{#if showBreakdown && grade.breakdown}
<div class="breakdown">
<div class="breakdown-item">
<span class="breakdown-label">Selection</span>
<span class="breakdown-value">{grade.breakdown.skillSelection}</span>
</div>
<div class="breakdown-item">
<span class="breakdown-label">Strength</span>
<span class="breakdown-value">{grade.breakdown.baseStrength}</span>
</div>
<div class="breakdown-item">
<span class="breakdown-label">Synergy</span>
<span class="breakdown-value">{grade.breakdown.synergy}</span>
</div>
</div>
{/if}
{#if showRecommendation && grade.recommendation}
{@const action = actionLabels[grade.recommendation.action]}
<div class="recommendation">
<span class="action-badge {action?.class ?? ''}">{action?.label ?? grade.recommendation.action}</span>
{#if grade.recommendation.reason}
<p class="reason">{grade.recommendation.reason}</p>
{/if}
</div>
{/if}
{:else}
<div class="no-grade">
<span class="no-grade-text">{grade.note ?? 'No grade'}</span>
</div>
{/if}
</div>
<style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
@use '$src/themes/layout' as layout;
.grade-display {
display: flex;
flex-direction: column;
gap: spacing.$unit;
}
.grade-header {
display: flex;
align-items: center;
gap: spacing.$unit;
}
.grade-letter {
font-size: 2rem;
font-weight: typography.$bold;
line-height: 1;
padding: spacing.$unit-half spacing.$unit;
border-radius: layout.$item-corner;
&.grade-s {
background: linear-gradient(135deg, #ffd700, #ffb347);
color: #6b4c00;
}
&.grade-a {
background: linear-gradient(135deg, #4ade80, #22c55e);
color: #14532d;
}
&.grade-b {
background: linear-gradient(135deg, #60a5fa, #3b82f6);
color: #1e3a5f;
}
&.grade-c {
background: colors.$grey-80;
color: colors.$grey-30;
}
&.grade-d {
background: colors.$grey-70;
color: colors.$grey-40;
}
&.grade-f {
background: linear-gradient(135deg, #f87171, #ef4444);
color: #7f1d1d;
}
&.grade-none {
background: colors.$grey-85;
color: colors.$grey-50;
}
}
.grade-score {
font-size: typography.$font-large;
font-weight: typography.$medium;
color: colors.$grey-40;
}
.breakdown {
display: flex;
gap: spacing.$unit-2x;
padding: spacing.$unit;
background: colors.$grey-95;
border-radius: layout.$item-corner;
}
.breakdown-item {
display: flex;
flex-direction: column;
gap: spacing.$unit-fourth;
flex: 1;
}
.breakdown-label {
font-size: typography.$font-small;
color: colors.$grey-50;
}
.breakdown-value {
font-size: typography.$font-regular;
font-weight: typography.$medium;
color: colors.$grey-30;
}
.recommendation {
display: flex;
flex-direction: column;
gap: spacing.$unit-half;
}
.action-badge {
display: inline-flex;
align-self: flex-start;
padding: spacing.$unit-fourth spacing.$unit-half;
border-radius: layout.$item-corner;
font-size: typography.$font-small;
font-weight: typography.$medium;
&.action-keep {
background: colors.$wind-bg-20;
color: colors.$wind-text-20;
}
&.action-reroll {
background: colors.$accent--yellow--100;
color: colors.$accent--yellow--10;
}
&.action-scrap {
background: colors.$error--bg--light;
color: colors.$error;
}
}
.reason {
font-size: typography.$font-small;
color: colors.$grey-40;
margin: 0;
}
.no-grade {
padding: spacing.$unit;
background: colors.$grey-95;
border-radius: layout.$item-corner;
}
.no-grade-text {
font-size: typography.$font-small;
color: colors.$grey-50;
font-style: italic;
}
// Size variants
.size-small {
.grade-letter {
font-size: 1.25rem;
padding: spacing.$unit-fourth spacing.$unit-half;
}
.grade-score {
font-size: typography.$font-regular;
}
}
.size-large {
.grade-letter {
font-size: 3rem;
padding: spacing.$unit spacing.$unit-2x;
}
.grade-score {
font-size: typography.$font-xlarge;
}
}
</style>

View file

@ -0,0 +1,178 @@
<svelte:options runes={true} />
<script lang="ts">
import type { ArtifactSkill } from '$lib/types/api/artifact'
import { createQuery } from '@tanstack/svelte-query'
import { artifactQueries } from '$lib/api/queries/artifact.queries'
interface Props {
/** Slot number (1-4) to get skills for */
slot: number
/** Currently selected modifier (for highlighting) */
selectedModifier?: number
/** Handler when a modifier is selected */
onSelect: (skill: ArtifactSkill) => void
}
const { slot, selectedModifier, onSelect }: Props = $props()
// Query skills for this slot
const skillsQuery = createQuery(() => artifactQueries.skillsForSlot(slot))
// Separate positive and negative skills
const positiveSkills = $derived(
skillsQuery.data?.filter((s) => s.polarity === 'positive') ?? []
)
const negativeSkills = $derived(
skillsQuery.data?.filter((s) => s.polarity === 'negative') ?? []
)
</script>
<div class="modifier-list">
{#if skillsQuery.isPending}
<div class="loading-state">Loading skills...</div>
{:else if skillsQuery.isError}
<div class="error-state">Failed to load skills</div>
{:else}
{#if positiveSkills.length > 0}
<div class="skill-group">
<h4 class="group-header positive">Positive Effects</h4>
<div class="skill-options">
{#each positiveSkills as skill (skill.id)}
<button
type="button"
class="modifier-option"
class:selected={selectedModifier === skill.modifier}
onclick={() => onSelect(skill)}
>
<span class="name">{skill.name.en}</span>
<span class="polarity positive">+</span>
</button>
{/each}
</div>
</div>
{/if}
{#if negativeSkills.length > 0}
<div class="skill-group">
<h4 class="group-header negative">Negative Effects</h4>
<div class="skill-options">
{#each negativeSkills as skill (skill.id)}
<button
type="button"
class="modifier-option"
class:selected={selectedModifier === skill.modifier}
onclick={() => onSelect(skill)}
>
<span class="name">{skill.name.en}</span>
<span class="polarity negative"></span>
</button>
{/each}
</div>
</div>
{/if}
{/if}
</div>
<style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
@use '$src/themes/layout' as layout;
@use '$src/themes/effects' as effects;
.modifier-list {
display: flex;
flex-direction: column;
gap: spacing.$unit-2x;
padding: spacing.$unit;
}
.loading-state,
.error-state {
padding: spacing.$unit-4x;
text-align: center;
color: colors.$grey-50;
}
.skill-group {
display: flex;
flex-direction: column;
gap: spacing.$unit;
}
.group-header {
font-size: typography.$font-small;
font-weight: typography.$medium;
text-transform: uppercase;
letter-spacing: 0.05em;
margin: 0;
padding: 0 spacing.$unit-half;
&.positive {
color: colors.$wind-text-20;
}
&.negative {
color: colors.$error;
}
}
.skill-options {
display: flex;
flex-direction: column;
gap: spacing.$unit-half;
}
.modifier-option {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: spacing.$unit;
background: transparent;
border: 1px solid transparent;
border-radius: layout.$item-corner;
cursor: pointer;
text-align: left;
transition:
background-color effects.$duration-quick ease,
border-color effects.$duration-quick ease;
&:hover {
background: colors.$grey-90;
}
&:active {
background: colors.$grey-85;
}
&.selected {
background: colors.$water-bg-20;
border-color: colors.$blue;
}
.name {
font-size: typography.$font-regular;
font-weight: typography.$normal;
color: colors.$grey-20;
}
.polarity {
font-size: typography.$font-small;
font-weight: typography.$bold;
padding: 2px 6px;
border-radius: 4px;
&.positive {
background: colors.$wind-bg-20;
color: colors.$wind-text-20;
}
&.negative {
background: colors.$error--bg--light;
color: colors.$error;
}
}
}
</style>

View file

@ -0,0 +1,223 @@
<svelte:options runes={true} />
<script lang="ts">
import DisclosureRow from '$lib/components/ui/DisclosureRow.svelte'
import Select from '$lib/components/ui/Select.svelte'
import type { ArtifactSkill, ArtifactSkillInstance } from '$lib/types/api/artifact'
interface Props {
/** Slot number (1-4) */
slot: number
/** Current skill configuration (null if not set) */
skill: ArtifactSkillInstance | null
/** Available skills for this slot (from query) */
availableSkills?: ArtifactSkill[]
/** Handler to open modifier selection pane */
onSelectModifier: () => void
/** Handler when skill is updated */
onUpdateSkill: (update: Partial<ArtifactSkillInstance>) => void
/** Whether editing is disabled */
disabled?: boolean
}
const {
slot,
skill,
availableSkills = [],
onSelectModifier,
onUpdateSkill,
disabled = false
}: Props = $props()
// Find the current skill definition
const currentSkillDef = $derived(
skill ? availableSkills.find((s) => s.modifier === skill.modifier) : null
)
// Get modifier name for display
const modifierName = $derived(currentSkillDef?.name?.en ?? 'Unknown')
// Build strength options from the skill's baseValues
const strengthOptions = $derived(() => {
if (!currentSkillDef?.baseValues) return []
return currentSkillDef.baseValues
.map((v, i) => ({
value: v ?? 0,
label: `${v}${currentSkillDef.suffix?.en ?? ''}`
}))
.filter((opt) => opt.value !== 0)
})
// Build level options (1-5)
const levelOptions = [
{ value: 1, label: '1' },
{ value: 2, label: '2' },
{ value: 3, label: '3' },
{ value: 4, label: '4' },
{ value: 5, label: '5' }
]
function handleStrengthChange(newStrength: number) {
onUpdateSkill({ strength: newStrength })
}
function handleLevelChange(newLevel: number) {
onUpdateSkill({ level: newLevel })
}
</script>
<div class="skill-row">
{#if !skill?.modifier}
<!-- Unset: Show disclosure to select modifier -->
<DisclosureRow
label="Skill {slot}"
sublabel="Tap to select"
onclick={onSelectModifier}
{disabled}
/>
{:else}
<!-- Set: Show modifier name + value/level controls -->
<div class="skill-row-set" class:disabled>
<div class="skill-header">
<div class="modifier-info">
<span class="modifier-name">{modifierName}</span>
{#if currentSkillDef?.polarity === 'negative'}
<span class="polarity negative"></span>
{:else}
<span class="polarity positive">+</span>
{/if}
</div>
<button
type="button"
class="change-btn"
onclick={onSelectModifier}
{disabled}
>
Change
</button>
</div>
<div class="skill-controls">
<div class="control-group">
<label class="control-label">Value</label>
<Select
value={skill.strength}
options={strengthOptions()}
size="small"
contained
onValueChange={(v) => v !== undefined && handleStrengthChange(v)}
{disabled}
/>
</div>
<div class="control-group">
<label class="control-label">Level</label>
<Select
value={skill.level}
options={levelOptions}
size="small"
contained
onValueChange={(v) => v !== undefined && handleLevelChange(v)}
{disabled}
/>
</div>
</div>
</div>
{/if}
</div>
<style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
@use '$src/themes/layout' as layout;
@use '$src/themes/effects' as effects;
.skill-row {
// Minimal container for skill row
display: contents;
}
.skill-row-set {
display: flex;
flex-direction: column;
gap: spacing.$unit;
padding: spacing.$unit;
background: var(--input-bg);
border-radius: layout.$item-corner;
&.disabled {
opacity: 0.6;
pointer-events: none;
}
}
.skill-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.modifier-info {
display: flex;
align-items: center;
gap: spacing.$unit-half;
}
.modifier-name {
font-weight: typography.$medium;
color: colors.$grey-20;
}
.polarity {
font-size: typography.$font-small;
font-weight: typography.$bold;
padding: 2px 4px;
border-radius: 4px;
&.positive {
background: colors.$wind-bg-20;
color: colors.$wind-text-20;
}
&.negative {
background: colors.$error--bg--light;
color: colors.$error;
}
}
.change-btn {
font-size: typography.$font-small;
color: colors.$blue;
background: none;
border: none;
cursor: pointer;
padding: spacing.$unit-fourth spacing.$unit-half;
border-radius: layout.$item-corner;
transition: background-color effects.$duration-quick ease;
&:hover {
background: colors.$grey-90;
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
}
.skill-controls {
display: flex;
gap: spacing.$unit-2x;
}
.control-group {
display: flex;
flex-direction: column;
gap: spacing.$unit-fourth;
flex: 1;
}
.control-label {
font-size: typography.$font-small;
color: colors.$grey-50;
}
</style>

View file

@ -0,0 +1,139 @@
<svelte:options runes={true} />
<script lang="ts">
import Icon from './Icon.svelte'
interface Props {
/** Primary label text */
label: string
/** Optional secondary label/description */
sublabel?: string
/** Value displayed on the right side (before the chevron) */
value?: string
/** Click handler - typically pushes a new pane */
onclick?: () => void
/** Whether the row is disabled */
disabled?: boolean
/** Element color for styling (optional) */
element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
}
const { label, sublabel, value, onclick, disabled = false, element }: Props = $props()
</script>
<button
type="button"
class="disclosure-row"
class:disabled
class:element-wind={element === 'wind'}
class:element-fire={element === 'fire'}
class:element-water={element === 'water'}
class:element-earth={element === 'earth'}
class:element-dark={element === 'dark'}
class:element-light={element === 'light'}
{onclick}
{disabled}
>
<div class="label-container">
<span class="label">{label}</span>
{#if sublabel}
<span class="sublabel">{sublabel}</span>
{/if}
</div>
<div class="value-area">
{#if value}
<span class="value">{value}</span>
{/if}
<Icon name="chevron-right" size={16} />
</div>
</button>
<style lang="scss">
@use '$src/themes/colors' as colors;
@use '$src/themes/layout' as layout;
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
@use '$src/themes/effects' as effects;
.disclosure-row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: spacing.$unit spacing.$unit;
margin: 0 calc(spacing.$unit * -1);
border-radius: layout.$item-corner;
font-size: typography.$font-regular;
min-height: calc(spacing.$unit * 5);
background: transparent;
border: none;
cursor: pointer;
text-align: left;
transition: background-color effects.$duration-quick ease;
&:hover:not(.disabled) {
background: colors.$grey-90;
}
&:active:not(.disabled) {
background: colors.$grey-85;
}
&.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.label-container {
display: flex;
flex-direction: column;
flex-shrink: 0;
margin-right: spacing.$unit-2x;
gap: spacing.$unit-fourth;
}
.label {
font-weight: typography.$medium;
color: colors.$grey-30;
}
.sublabel {
font-size: typography.$font-small;
color: colors.$grey-60;
font-weight: typography.$normal;
}
.value-area {
display: flex;
align-items: center;
gap: spacing.$unit-half;
color: colors.$grey-50;
flex-shrink: 0;
}
.value {
font-size: typography.$font-regular;
color: colors.$grey-50;
}
// Element-colored value
&.element-wind .value {
color: colors.$wind-text-20;
}
&.element-fire .value {
color: colors.$fire-text-20;
}
&.element-water .value {
color: colors.$water-text-20;
}
&.element-earth .value {
color: colors.$earth-text-20;
}
&.element-dark .value {
color: colors.$dark-text-20;
}
&.element-light .value {
color: colors.$light-text-20;
}
}
</style>