add DatabasePageHeader to entity detail/edit pages

This commit is contained in:
Justin Edmund 2025-12-23 13:41:50 -08:00
parent 5c2203af42
commit 2898740cb2
7 changed files with 177 additions and 27 deletions

View file

@ -0,0 +1,75 @@
<svelte:options runes={true} />
<script lang="ts">
import Button from '$lib/components/ui/Button.svelte'
import type { Snippet } from 'svelte'
interface Props {
/** Page title (not the item name) */
title: string
/** Custom right action content */
rightAction?: Snippet | undefined
/** Click handler for back button - defaults to history.back() */
onBack?: (() => void) | undefined
}
let { title, rightAction, onBack }: Props = $props()
function handleBack() {
if (onBack) {
onBack()
} else {
history.back()
}
}
</script>
<header class="header">
<div class="left">
<Button variant="ghost" size="small" leftIcon="chevron-left" onclick={handleBack}>
Back
</Button>
</div>
<h1 class="title">{title}</h1>
<div class="right">
{#if rightAction}
{@render rightAction()}
{/if}
</div>
</header>
<style lang="scss">
@use '$src/themes/spacing' as spacing;
@use '$src/themes/typography' as typography;
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: spacing.$unit-2x;
gap: spacing.$unit-2x;
}
.left,
.right {
flex: 1;
min-width: 0;
}
.right {
display: flex;
justify-content: flex-end;
gap: spacing.$unit;
}
.title {
flex: 1;
text-align: center;
font-size: typography.$font-small;
font-weight: typography.$medium;
color: var(--text-secondary);
margin: 0;
}
</style>

View file

@ -38,6 +38,7 @@
} from '$lib/utils/external-links'
import Button from '$lib/components/ui/Button.svelte'
import CharacterTags from '$lib/components/tags/CharacterTags.svelte'
import DatabasePageHeader from '$lib/components/database/DatabasePageHeader.svelte'
// Types
import type { PageData } from './$types'
@ -192,13 +193,19 @@
<PageMeta title={pageTitle} description={m.page_desc_home()} />
<div class="page">
<DatabasePageHeader title="Character">
{#snippet rightAction()}
{#if canEdit && editUrl}
<Button variant="element-ghost" element={elementName} size="small" href={editUrl}>Edit</Button>
{/if}
{/snippet}
</DatabasePageHeader>
{#if character}
<DetailScaffold
type="character"
item={character}
image={getCharacterGridImage(character)}
showEdit={canEdit}
editUrl={canEdit ? editUrl : undefined}
{currentTab}
onTabChange={handleTabChange}
onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
@ -379,7 +386,7 @@
.page {
background: white;
border-radius: layout.$card-corner;
border-radius: layout.$page-corner;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

View file

@ -25,12 +25,15 @@
import TagInput from '$lib/components/ui/TagInput.svelte'
import { getCharacterImage } from '$lib/utils/images'
import { CHARACTER_SERIES_NAMES } from '$lib/types/enums'
import { getElementLabel } from '$lib/utils/element'
import {
buildWikiEnUrl,
buildWikiJaUrl,
buildGamewithUrl,
buildKamigameUrl
} from '$lib/utils/external-links'
import DatabasePageHeader from '$lib/components/database/DatabasePageHeader.svelte'
import Button from '$lib/components/ui/Button.svelte'
// Types
import type { PageData } from './$types'
@ -56,6 +59,18 @@
// Always in edit mode
const editMode = true
// Element for button styling
const elementName = $derived(
getElementLabel(character?.element)?.toLowerCase() as
| 'wind'
| 'fire'
| 'water'
| 'earth'
| 'dark'
| 'light'
| undefined
)
// Save state
let isSaving = $state(false)
let saveError = $state<string | null>(null)
@ -277,17 +292,20 @@
<PageMeta title={pageTitle} description={m.page_desc_home()} />
<div class="page">
<DatabasePageHeader title="Edit Character" onBack={handleCancel}>
{#snippet rightAction()}
<Button variant="element-ghost" element={elementName} size="small" onclick={saveChanges} disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save'}
</Button>
{/snippet}
</DatabasePageHeader>
{#if character}
<DetailScaffold
type="character"
item={character}
image={getCharacterGridImage(character)}
showEdit={true}
{editMode}
{isSaving}
{saveError}
onSave={saveChanges}
onCancel={handleCancel}
>
<section class="details">
<CharacterMetadataSection {character} {editMode} bind:editData />
@ -394,7 +412,7 @@
.page {
background: white;
border-radius: layout.$card-corner;
border-radius: layout.$page-corner;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

View file

@ -36,6 +36,7 @@
buildKamigameUrl
} from '$lib/utils/external-links'
import Button from '$lib/components/ui/Button.svelte'
import DatabasePageHeader from '$lib/components/database/DatabasePageHeader.svelte'
// Types
import type { PageData } from './$types'
@ -184,13 +185,19 @@
<PageMeta title={pageTitle} description={m.page_desc_home()} />
<div class="page">
<DatabasePageHeader title="Summon">
{#snippet rightAction()}
{#if canEdit && editUrl}
<Button variant="element-ghost" element={elementName} size="small" href={editUrl}>Edit</Button>
{/if}
{/snippet}
</DatabasePageHeader>
{#if summon}
<DetailScaffold
type="summon"
item={summon}
image={getSummonGridImage(summon)}
showEdit={canEdit}
editUrl={canEdit ? editUrl : undefined}
{currentTab}
onTabChange={handleTabChange}
onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
@ -369,7 +376,7 @@
.page {
background: white;
border-radius: layout.$card-corner;
border-radius: layout.$page-corner;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

View file

@ -27,6 +27,9 @@
buildGamewithUrl,
buildKamigameUrl
} from '$lib/utils/external-links'
import { getElementLabel } from '$lib/utils/element'
import DatabasePageHeader from '$lib/components/database/DatabasePageHeader.svelte'
import Button from '$lib/components/ui/Button.svelte'
// Types
import type { PageData } from './$types'
@ -47,6 +50,18 @@
// Always in edit mode
const editMode = true
// Element for button styling
const elementName = $derived(
getElementLabel(summon?.element)?.toLowerCase() as
| 'wind'
| 'fire'
| 'water'
| 'earth'
| 'dark'
| 'light'
| undefined
)
// Save state
let isSaving = $state(false)
let saveError = $state<string | null>(null)
@ -201,17 +216,20 @@
</script>
<div class="page">
<DatabasePageHeader title="Edit Summon" onBack={handleCancel}>
{#snippet rightAction()}
<Button variant="element-ghost" element={elementName} size="small" onclick={saveChanges} disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save'}
</Button>
{/snippet}
</DatabasePageHeader>
{#if summon}
<DetailScaffold
type="summon"
item={summon}
image={getSummonGridImage(summon)}
showEdit={true}
{editMode}
{isSaving}
{saveError}
onSave={saveChanges}
onCancel={handleCancel}
>
<section class="details">
<SummonMetadataSection {summon} {editMode} bind:editData />
@ -327,7 +345,7 @@
.page {
background: white;
border-radius: layout.$card-corner;
border-radius: layout.$page-corner;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

View file

@ -39,6 +39,7 @@
buildKamigameUrl
} from '$lib/utils/external-links'
import Button from '$lib/components/ui/Button.svelte'
import DatabasePageHeader from '$lib/components/database/DatabasePageHeader.svelte'
// Types
import type { PageData } from './$types'
@ -186,13 +187,19 @@
<PageMeta title={pageTitle} description={m.page_desc_home()} />
<div class="page">
<DatabasePageHeader title="Weapon">
{#snippet rightAction()}
{#if canEdit && editUrl}
<Button variant="element-ghost" element={elementName} size="small" href={editUrl}>Edit</Button>
{/if}
{/snippet}
</DatabasePageHeader>
{#if weapon}
<DetailScaffold
type="weapon"
item={weapon}
image={getWeaponImage(weapon)}
showEdit={canEdit}
editUrl={canEdit ? editUrl : undefined}
{currentTab}
onTabChange={handleTabChange}
onDownloadAllImages={canEdit ? handleDownloadAllImages : undefined}
@ -372,7 +379,7 @@
.page {
background: white;
border-radius: layout.$card-corner;
border-radius: layout.$page-corner;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

View file

@ -28,6 +28,9 @@
buildGamewithUrl,
buildKamigameUrl
} from '$lib/utils/external-links'
import { getElementLabel } from '$lib/utils/element'
import DatabasePageHeader from '$lib/components/database/DatabasePageHeader.svelte'
import Button from '$lib/components/ui/Button.svelte'
// Types
import type { PageData } from './$types'
@ -48,6 +51,18 @@
// Always in edit mode
const editMode = true
// Element for button styling
const elementName = $derived(
getElementLabel(weapon?.element)?.toLowerCase() as
| 'wind'
| 'fire'
| 'water'
| 'earth'
| 'dark'
| 'light'
| undefined
)
// Save state
let isSaving = $state(false)
let saveError = $state<string | null>(null)
@ -224,17 +239,20 @@
</script>
<div class="page">
<DatabasePageHeader title="Edit Weapon" onBack={handleCancel}>
{#snippet rightAction()}
<Button variant="element-ghost" element={elementName} size="small" onclick={saveChanges} disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save'}
</Button>
{/snippet}
</DatabasePageHeader>
{#if weapon}
<DetailScaffold
type="weapon"
item={weapon}
image={getWeaponImage(weapon)}
showEdit={true}
{editMode}
{isSaving}
{saveError}
onSave={saveChanges}
onCancel={handleCancel}
>
<section class="details">
<WeaponMetadataSection {weapon} {editMode} bind:editData />
@ -351,7 +369,7 @@
.page {
background: white;
border-radius: layout.$card-corner;
border-radius: layout.$page-corner;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}