refactor character edit page to use section components
- use DetailScaffold wrapper - extract metadata, uncap, taxonomy, stats sections - standardize field naming (camelCase) - add element-themed checkboxes
This commit is contained in:
parent
817084cee5
commit
b58cbbe72f
5 changed files with 98 additions and 360 deletions
|
|
@ -5,11 +5,13 @@
|
|||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import { getRarityLabel, getRarityOptions } from '$lib/utils/rarity'
|
||||
|
||||
let {
|
||||
character,
|
||||
editMode = false,
|
||||
editData = $bindable<any>()
|
||||
}: { character: any; editMode?: boolean; editData?: any } = $props()
|
||||
interface Props {
|
||||
character: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
}
|
||||
|
||||
let { character, editMode = false, editData = $bindable() }: Props = $props()
|
||||
|
||||
const rarityOptions = getRarityOptions()
|
||||
</script>
|
||||
|
|
@ -17,10 +19,11 @@
|
|||
<DetailsContainer title="Metadata">
|
||||
{#if editMode}
|
||||
<DetailItem label="Rarity" bind:value={editData.rarity} editable={true} type="select" options={rarityOptions} />
|
||||
<DetailItem label="Granblue ID" bind:value={editData.granblue_id} editable={true} type="text" />
|
||||
<DetailItem label="Granblue ID" bind:value={editData.granblueId} editable={true} type="text" />
|
||||
<DetailItem label="Character ID" bind:value={editData.characterId} editable={true} type="number" />
|
||||
{:else}
|
||||
<DetailItem label="Rarity" value={getRarityLabel(character.rarity)} />
|
||||
<DetailItem label="Granblue ID" value={character.granblue_id} />
|
||||
<DetailItem label="Granblue ID" value={character.granblueId} />
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,39 +4,41 @@
|
|||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
|
||||
let {
|
||||
character,
|
||||
editMode = false,
|
||||
editData = $bindable<any>()
|
||||
}: { character: any; editMode?: boolean; editData?: any } = $props()
|
||||
interface Props {
|
||||
character: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
}
|
||||
|
||||
let { character, editMode = false, editData = $bindable() }: Props = $props()
|
||||
|
||||
const flb = $derived(editMode ? Boolean(editData.flb) : Boolean(character?.uncap?.flb))
|
||||
</script>
|
||||
|
||||
<DetailsContainer title="HP Stats">
|
||||
{#if editMode}
|
||||
<DetailItem label="Base HP" bind:value={editData.min_hp} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Max HP" bind:value={editData.max_hp} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Max HP (FLB)" bind:value={editData.max_hp_flb} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Base HP" bind:value={editData.minHp} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Max HP" bind:value={editData.maxHp} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Max HP (FLB)" bind:value={editData.maxHpFlb} editable={true} type="number" placeholder="0" />
|
||||
{:else}
|
||||
<DetailItem label="Base HP" value={character.hp?.min_hp} />
|
||||
<DetailItem label="Max HP" value={character.hp?.max_hp} />
|
||||
<DetailItem label="Base HP" value={character.hp?.minHp} />
|
||||
<DetailItem label="Max HP" value={character.hp?.maxHp} />
|
||||
{#if flb}
|
||||
<DetailItem label="Max HP (FLB)" value={character.hp?.max_hp_flb} />
|
||||
{/if}
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
||||
<DetailsContainer title="Attack Stats">
|
||||
{#if editMode}
|
||||
<DetailItem label="Base Attack" bind:value={editData.min_atk} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Max Attack" bind:value={editData.max_atk} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Max Attack (FLB)" bind:value={editData.max_atk_flb} editable={true} type="number" placeholder="0" />
|
||||
{:else}
|
||||
<DetailItem label="Base Attack" value={character.atk?.min_atk} />
|
||||
<DetailItem label="Max Attack" value={character.atk?.max_atk} />
|
||||
{#if flb}
|
||||
<DetailItem label="Max Attack (FLB)" value={character.atk?.max_atk_flb} />
|
||||
<DetailItem label="Max HP (FLB)" value={character.hp?.maxHpFlb} />
|
||||
{/if}
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
||||
<DetailsContainer title="Attack Stats">
|
||||
{#if editMode}
|
||||
<DetailItem label="Base Attack" bind:value={editData.minAtk} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Max Attack" bind:value={editData.maxAtk} editable={true} type="number" placeholder="0" />
|
||||
<DetailItem label="Max Attack (FLB)" bind:value={editData.maxAtkFlb} editable={true} type="number" placeholder="0" />
|
||||
{:else}
|
||||
<DetailItem label="Base Attack" value={character.atk?.minAtk} />
|
||||
<DetailItem label="Max Attack" value={character.atk?.maxAtk} />
|
||||
{#if flb}
|
||||
<DetailItem label="Max Attack (FLB)" value={character.atk?.maxAtkFlb} />
|
||||
{/if}
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
|
|
|||
|
|
@ -8,11 +8,13 @@
|
|||
import { getGenderLabel, getGenderOptions } from '$lib/utils/gender'
|
||||
import { getProficiencyLabel, getProficiencyOptions } from '$lib/utils/proficiency'
|
||||
|
||||
let {
|
||||
character,
|
||||
editMode = false,
|
||||
editData = $bindable<any>()
|
||||
}: { character: any; editMode?: boolean; editData?: any } = $props()
|
||||
interface Props {
|
||||
character: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
}
|
||||
|
||||
let { character, editMode = false, editData = $bindable() }: Props = $props()
|
||||
|
||||
const elementOptions = getElementOptions()
|
||||
const raceOptions = getRaceOptions()
|
||||
|
|
@ -35,8 +37,8 @@
|
|||
<DetailItem label="Race 2" value={getRaceLabel(character.race?.[1])} />
|
||||
{/if}
|
||||
<DetailItem label="Gender" value={getGenderLabel(character.gender)} />
|
||||
<DetailItem label="Proficiency 1" value={getProficiencyLabel(character.proficiency[0])} />
|
||||
<DetailItem label="Proficiency 2" value={getProficiencyLabel(character.proficiency[1])} />
|
||||
<DetailItem label="Proficiency 1" value={getProficiencyLabel(character.proficiency?.[0] ?? 0)} />
|
||||
<DetailItem label="Proficiency 2" value={getProficiencyLabel(character.proficiency?.[1] ?? 0)} />
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,17 @@
|
|||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import UncapIndicator from '$lib/components/uncap/UncapIndicator.svelte'
|
||||
import { getCharacterMaxUncapLevel } from '$lib/utils/uncap'
|
||||
import { getElementLabel } from '$lib/utils/element'
|
||||
|
||||
let {
|
||||
character,
|
||||
editMode = false,
|
||||
editData = $bindable<any>()
|
||||
}: { character: any; editMode?: boolean; editData?: any } = $props()
|
||||
type ElementName = 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
||||
|
||||
interface Props {
|
||||
character: any
|
||||
editMode?: boolean
|
||||
editData?: any
|
||||
}
|
||||
|
||||
let { character, editMode = false, editData = $bindable() }: Props = $props()
|
||||
|
||||
const uncap = $derived(
|
||||
editMode
|
||||
|
|
@ -23,10 +28,19 @@
|
|||
const special = $derived(editMode ? editData.special : (character?.special ?? false))
|
||||
const uncapLevel = $derived(getCharacterMaxUncapLevel({ special, uncap }))
|
||||
const transcendenceStage = $derived(transcendence ? 5 : 0)
|
||||
|
||||
// Get element name for checkbox theming
|
||||
const elementName = $derived.by((): ElementName | undefined => {
|
||||
const el = editMode ? editData.element : character?.element
|
||||
const label = getElementLabel(el)
|
||||
return label !== '—' && label !== 'Null'
|
||||
? (label.toLowerCase() as ElementName)
|
||||
: undefined
|
||||
})
|
||||
</script>
|
||||
|
||||
<DetailsContainer title="Details">
|
||||
{#if character.uncap}
|
||||
<DetailsContainer title="Uncap">
|
||||
{#if character?.uncap || editMode}
|
||||
<DetailItem label="Uncap">
|
||||
<UncapIndicator
|
||||
type="character"
|
||||
|
|
@ -42,10 +56,10 @@
|
|||
{/if}
|
||||
|
||||
{#if editMode}
|
||||
<DetailItem label="FLB" bind:value={editData.flb} editable={true} type="checkbox" />
|
||||
<DetailItem label="ULB" bind:value={editData.ulb} editable={true} type="checkbox" />
|
||||
<DetailItem label="Transcendence" bind:value={editData.transcendence} editable={true} type="checkbox" />
|
||||
<DetailItem label="Special" bind:value={editData.special} editable={true} type="checkbox" />
|
||||
<DetailItem label="FLB" bind:value={editData.flb} editable={true} type="checkbox" element={elementName} />
|
||||
<DetailItem label="ULB" bind:value={editData.ulb} editable={true} type="checkbox" element={elementName} />
|
||||
<DetailItem label="Transcendence" bind:value={editData.transcendence} editable={true} type="checkbox" element={elementName} />
|
||||
<DetailItem label="Special" bind:value={editData.special} editable={true} type="checkbox" element={elementName} />
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,20 +10,13 @@
|
|||
import { entityAdapter } from '$lib/api/adapters/entity.adapter'
|
||||
import { withInitialData } from '$lib/query/ssr'
|
||||
|
||||
// Utility functions
|
||||
import { getRarityLabel, getRarityOptions } from '$lib/utils/rarity'
|
||||
import { getElementLabel, getElementOptions } from '$lib/utils/element'
|
||||
import { getProficiencyLabel, getProficiencyOptions } from '$lib/utils/proficiency'
|
||||
import { getRaceLabel, getRaceOptions } from '$lib/utils/race'
|
||||
import { getGenderLabel, getGenderOptions } from '$lib/utils/gender'
|
||||
import { getCharacterMaxUncapLevel } from '$lib/utils/uncap'
|
||||
|
||||
// Components
|
||||
import UncapIndicator from '$lib/components/uncap/UncapIndicator.svelte'
|
||||
import DetailScaffold from '$lib/features/database/detail/DetailScaffold.svelte'
|
||||
import CharacterMetadataSection from '$lib/features/database/characters/sections/CharacterMetadataSection.svelte'
|
||||
import CharacterUncapSection from '$lib/features/database/characters/sections/CharacterUncapSection.svelte'
|
||||
import CharacterTaxonomySection from '$lib/features/database/characters/sections/CharacterTaxonomySection.svelte'
|
||||
import CharacterStatsSection from '$lib/features/database/characters/sections/CharacterStatsSection.svelte'
|
||||
import DetailsContainer from '$lib/components/ui/DetailsContainer.svelte'
|
||||
import DetailItem from '$lib/components/ui/DetailItem.svelte'
|
||||
import DetailsHeader from '$lib/components/ui/DetailsHeader.svelte'
|
||||
import Button from '$lib/components/ui/Button.svelte'
|
||||
import { getCharacterImage } from '$lib/utils/images'
|
||||
|
||||
// Types
|
||||
|
|
@ -54,6 +47,7 @@
|
|||
},
|
||||
enabled: !!character?.characterId && !editMode
|
||||
}))
|
||||
|
||||
let isSaving = $state(false)
|
||||
let saveError = $state<string | null>(null)
|
||||
let saveSuccess = $state(false)
|
||||
|
|
@ -110,13 +104,6 @@
|
|||
}
|
||||
})
|
||||
|
||||
// Options for select dropdowns - using centralized utilities
|
||||
const rarityOptions = getRarityOptions()
|
||||
const elementOptions = getElementOptions()
|
||||
const raceOptions = getRaceOptions()
|
||||
const genderOptions = getGenderOptions()
|
||||
const proficiencyOptions = getProficiencyOptions()
|
||||
|
||||
function toggleEditMode() {
|
||||
editMode = !editMode
|
||||
saveError = null
|
||||
|
|
@ -155,7 +142,7 @@
|
|||
saveSuccess = false
|
||||
|
||||
try {
|
||||
// Prepare the data for API
|
||||
// Prepare the data for API (convert to snake_case)
|
||||
const payload = {
|
||||
name: editData.name,
|
||||
granblue_id: editData.granblueId,
|
||||
|
|
@ -208,264 +195,32 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Helper function to get character image
|
||||
// Helper function for character grid image
|
||||
function getCharacterGridImage(character: any): string {
|
||||
return getCharacterImage(character?.granblueId, 'grid', '01')
|
||||
}
|
||||
|
||||
// Calculate uncap properties for the indicator
|
||||
const uncap = $derived(
|
||||
editMode
|
||||
? { flb: editData.flb, ulb: editData.ulb, transcendence: editData.transcendence }
|
||||
: (character?.uncap ?? { flb: false, ulb: false, transcendence: false })
|
||||
)
|
||||
const flb = $derived(uncap.flb ?? false)
|
||||
const ulb = $derived(uncap.ulb ?? false)
|
||||
const transcendence = $derived(uncap.transcendence ?? false)
|
||||
const special = $derived(editMode ? editData.special : (character?.special ?? false))
|
||||
|
||||
const uncapLevel = $derived(
|
||||
getCharacterMaxUncapLevel({ special, uncap: { flb, ulb, transcendence } })
|
||||
)
|
||||
const transcendenceStage = $derived(transcendence ? 5 : 0)
|
||||
|
||||
// Get element name for checkbox theming
|
||||
const elementName = $derived.by(() => {
|
||||
const el = editMode ? editData.element : character?.element
|
||||
const label = getElementLabel(el)
|
||||
return label !== '—' && label !== 'Null'
|
||||
? (label.toLowerCase() as 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light')
|
||||
: undefined
|
||||
})
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if character}
|
||||
<div class="content">
|
||||
<DetailsHeader
|
||||
type="character"
|
||||
item={character}
|
||||
image={getCharacterGridImage(character)}
|
||||
onEdit={toggleEditMode}
|
||||
showEdit={canEdit}
|
||||
{editMode}
|
||||
onSave={saveChanges}
|
||||
onCancel={toggleEditMode}
|
||||
{isSaving}
|
||||
/>
|
||||
|
||||
{#if saveSuccess || saveError}
|
||||
<div class="edit-controls">
|
||||
{#if saveSuccess}
|
||||
<span class="success-message">Changes saved successfully!</span>
|
||||
{/if}
|
||||
|
||||
{#if saveError}
|
||||
<span class="error-message">{saveError}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<DetailScaffold
|
||||
type="character"
|
||||
item={character}
|
||||
image={getCharacterGridImage(character)}
|
||||
showEdit={canEdit}
|
||||
{editMode}
|
||||
{isSaving}
|
||||
{saveSuccess}
|
||||
{saveError}
|
||||
onEdit={toggleEditMode}
|
||||
onSave={saveChanges}
|
||||
onCancel={toggleEditMode}
|
||||
>
|
||||
<section class="details">
|
||||
<DetailsContainer title="Metadata">
|
||||
{#if editMode}
|
||||
<DetailItem
|
||||
label="Rarity"
|
||||
bind:value={editData.rarity}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={rarityOptions}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Granblue ID"
|
||||
bind:value={editData.granblueId}
|
||||
editable={true}
|
||||
type="text"
|
||||
/>
|
||||
<DetailItem
|
||||
label="Character ID"
|
||||
bind:value={editData.characterId}
|
||||
editable={true}
|
||||
type="number"
|
||||
/>
|
||||
{:else}
|
||||
<DetailItem label="Rarity" value={getRarityLabel(character.rarity)} />
|
||||
<DetailItem label="Granblue ID" value={character.granblueId} />
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
||||
<DetailsContainer title="Details">
|
||||
{#if character.uncap}
|
||||
<DetailItem label="Uncap">
|
||||
<UncapIndicator
|
||||
type="character"
|
||||
{uncapLevel}
|
||||
{transcendenceStage}
|
||||
{flb}
|
||||
{ulb}
|
||||
{transcendence}
|
||||
{special}
|
||||
editable={false}
|
||||
/>
|
||||
</DetailItem>
|
||||
{/if}
|
||||
|
||||
{#if editMode}
|
||||
<DetailItem
|
||||
label="FLB"
|
||||
bind:value={editData.flb}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
/>
|
||||
<DetailItem
|
||||
label="ULB"
|
||||
bind:value={editData.ulb}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Transcendence"
|
||||
bind:value={editData.transcendence}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Special"
|
||||
bind:value={editData.special}
|
||||
editable={true}
|
||||
type="checkbox"
|
||||
element={elementName}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if editMode}
|
||||
<DetailItem
|
||||
label="Element"
|
||||
bind:value={editData.element}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={elementOptions}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Race 1"
|
||||
bind:value={editData.race1}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={raceOptions}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Race 2"
|
||||
bind:value={editData.race2}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={raceOptions}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Gender"
|
||||
bind:value={editData.gender}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={genderOptions}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Proficiency 1"
|
||||
bind:value={editData.proficiency1}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={proficiencyOptions}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Proficiency 2"
|
||||
bind:value={editData.proficiency2}
|
||||
editable={true}
|
||||
type="select"
|
||||
options={proficiencyOptions}
|
||||
/>
|
||||
{:else}
|
||||
<DetailItem label="Element" value={getElementLabel(character.element)} />
|
||||
<DetailItem label="Race 1" value={getRaceLabel(character.race?.[0])} />
|
||||
{#if character.race?.[1]}
|
||||
<DetailItem label="Race 2" value={getRaceLabel(character.race?.[1])} />
|
||||
{/if}
|
||||
<DetailItem label="Gender" value={getGenderLabel(character.gender)} />
|
||||
<DetailItem
|
||||
label="Proficiency 1"
|
||||
value={getProficiencyLabel(character.proficiency?.[0] ?? 0)}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Proficiency 2"
|
||||
value={getProficiencyLabel(character.proficiency?.[1] ?? 0)}
|
||||
/>
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
||||
<DetailsContainer title="HP Stats">
|
||||
{#if editMode}
|
||||
<DetailItem
|
||||
label="Base HP"
|
||||
bind:value={editData.minHp}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
/>
|
||||
<DetailItem
|
||||
label="Max HP"
|
||||
bind:value={editData.maxHp}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
/>
|
||||
<DetailItem
|
||||
label="Max HP (FLB)"
|
||||
bind:value={editData.maxHpFlb}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
/>
|
||||
{:else}
|
||||
<DetailItem label="Base HP" value={character.hp?.minHp} />
|
||||
<DetailItem label="Max HP" value={character.hp?.maxHp} />
|
||||
{#if flb}
|
||||
<DetailItem label="Max HP (FLB)" value={character.hp?.maxHpFlb} />
|
||||
{/if}
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
|
||||
<DetailsContainer title="Attack Stats">
|
||||
{#if editMode}
|
||||
<DetailItem
|
||||
label="Base Attack"
|
||||
bind:value={editData.minAtk}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
/>
|
||||
<DetailItem
|
||||
label="Max Attack"
|
||||
bind:value={editData.maxAtk}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
/>
|
||||
<DetailItem
|
||||
label="Max Attack (FLB)"
|
||||
bind:value={editData.maxAtkFlb}
|
||||
editable={true}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
/>
|
||||
{:else}
|
||||
<DetailItem label="Base Attack" value={character.atk?.minAtk} />
|
||||
<DetailItem label="Max Attack" value={character.atk?.maxAtk} />
|
||||
{#if flb}
|
||||
<DetailItem label="Max Attack (FLB)" value={character.atk?.maxAtkFlb} />
|
||||
{/if}
|
||||
{/if}
|
||||
</DetailsContainer>
|
||||
<CharacterMetadataSection {character} {editMode} bind:editData />
|
||||
<CharacterUncapSection {character} {editMode} bind:editData />
|
||||
<CharacterTaxonomySection {character} {editMode} bind:editData />
|
||||
<CharacterStatsSection {character} {editMode} bind:editData />
|
||||
|
||||
{#if !editMode && relatedQuery.data?.length}
|
||||
<DetailsContainer title="Related Units">
|
||||
|
|
@ -484,7 +239,7 @@
|
|||
</DetailsContainer>
|
||||
{/if}
|
||||
</section>
|
||||
</div>
|
||||
</DetailScaffold>
|
||||
{:else}
|
||||
<div class="not-found">
|
||||
<h2>Character Not Found</h2>
|
||||
|
|
@ -499,7 +254,6 @@
|
|||
@use '$src/themes/layout' as layout;
|
||||
@use '$src/themes/spacing' as spacing;
|
||||
@use '$src/themes/typography' as typography;
|
||||
@use '$src/themes/effects' as effects;
|
||||
|
||||
.not-found {
|
||||
text-align: center;
|
||||
|
|
@ -520,46 +274,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
background: white;
|
||||
border-radius: layout.$card-corner;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-controls {
|
||||
padding: spacing.$unit-2x;
|
||||
border-bottom: 1px solid colors.$grey-80;
|
||||
.details {
|
||||
display: flex;
|
||||
gap: spacing.$unit;
|
||||
align-items: center;
|
||||
|
||||
.success-message {
|
||||
color: colors.$grey-30;
|
||||
font-size: typography.$font-small;
|
||||
animation: fadeIn effects.$duration-opacity-fade ease-in;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: colors.$error;
|
||||
font-size: typography.$font-small;
|
||||
animation: fadeIn effects.$duration-opacity-fade ease-in;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.related-units {
|
||||
|
|
|
|||
Loading…
Reference in a new issue