21 changed files with 387 additions and 219 deletions
@ -0,0 +1,155 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import Icon from '../ui/Icon.svelte'; |
||||||
|
import { goto } from '$app/navigation'; |
||||||
|
import { page } from '$app/stores'; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
title: string; |
||||||
|
onRefresh?: () => void | Promise<void>; |
||||||
|
refreshLoading?: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
let { title, onRefresh, refreshLoading = false }: Props = $props(); |
||||||
|
|
||||||
|
function handleBack() { |
||||||
|
if (typeof window !== 'undefined' && window.history.length > 1) { |
||||||
|
window.history.back(); |
||||||
|
} else { |
||||||
|
goto('/discussions'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async function handleRefresh() { |
||||||
|
if (onRefresh) { |
||||||
|
await onRefresh(); |
||||||
|
} else { |
||||||
|
// Default: reload the page |
||||||
|
window.location.reload(); |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<div class="page-header"> |
||||||
|
<div class="page-header-left"> |
||||||
|
<button |
||||||
|
class="page-header-button back-button" |
||||||
|
onclick={handleBack} |
||||||
|
aria-label="Go back" |
||||||
|
title="Go back" |
||||||
|
> |
||||||
|
<Icon name="arrow-left" size={20} /> |
||||||
|
</button> |
||||||
|
<h1 class="page-header-title">{title}</h1> |
||||||
|
</div> |
||||||
|
<button |
||||||
|
class="page-header-button refresh-button" |
||||||
|
class:loading={refreshLoading} |
||||||
|
onclick={handleRefresh} |
||||||
|
disabled={refreshLoading} |
||||||
|
aria-label="Refresh" |
||||||
|
title="Refresh" |
||||||
|
> |
||||||
|
<Icon name="refresh" size={20} /> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
<style> |
||||||
|
.page-header { |
||||||
|
display: flex; |
||||||
|
justify-content: space-between; |
||||||
|
align-items: center; |
||||||
|
gap: 1rem; |
||||||
|
padding: 0 1rem; |
||||||
|
margin-bottom: 1.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
.page-header-left { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
gap: 0.75rem; |
||||||
|
flex: 1; |
||||||
|
min-width: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.page-header-title { |
||||||
|
font-size: 1.5em; |
||||||
|
font-weight: bold; |
||||||
|
color: var(--fog-text, #1f2937); |
||||||
|
margin: 0; |
||||||
|
font-family: monospace; |
||||||
|
flex: 1; |
||||||
|
min-width: 0; |
||||||
|
word-break: break-word; |
||||||
|
} |
||||||
|
|
||||||
|
:global(.dark) .page-header-title { |
||||||
|
color: var(--fog-dark-text, #f9fafb); |
||||||
|
} |
||||||
|
|
||||||
|
.page-header-button { |
||||||
|
display: inline-flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
padding: 0.5rem; |
||||||
|
border: 1px solid var(--fog-border, #e5e7eb); |
||||||
|
border-radius: 0.375rem; |
||||||
|
background: var(--fog-post, #ffffff); |
||||||
|
color: var(--fog-text, #1f2937); |
||||||
|
cursor: pointer; |
||||||
|
transition: all 0.2s; |
||||||
|
flex-shrink: 0; |
||||||
|
width: 2.5rem; |
||||||
|
height: 2.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
:global(.dark) .page-header-button { |
||||||
|
border-color: var(--fog-dark-border, #374151); |
||||||
|
background: var(--fog-dark-post, #1f2937); |
||||||
|
color: var(--fog-dark-text, #f9fafb); |
||||||
|
} |
||||||
|
|
||||||
|
.page-header-button:hover:not(:disabled) { |
||||||
|
background: var(--fog-highlight, #f3f4f6); |
||||||
|
border-color: var(--fog-accent, #64748b); |
||||||
|
} |
||||||
|
|
||||||
|
:global(.dark) .page-header-button:hover:not(:disabled) { |
||||||
|
background: var(--fog-dark-highlight, #475569); |
||||||
|
border-color: var(--fog-dark-accent, #94a3b8); |
||||||
|
} |
||||||
|
|
||||||
|
.page-header-button:disabled { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: not-allowed; |
||||||
|
} |
||||||
|
|
||||||
|
.page-header-button.loading { |
||||||
|
animation: spin 1s linear infinite; |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes spin { |
||||||
|
from { |
||||||
|
transform: rotate(0deg); |
||||||
|
} |
||||||
|
to { |
||||||
|
transform: rotate(360deg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@media (max-width: 640px) { |
||||||
|
.page-header { |
||||||
|
padding: 0 0.5rem; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.page-header-title { |
||||||
|
font-size: 1.25em; |
||||||
|
} |
||||||
|
|
||||||
|
.page-header-button { |
||||||
|
width: 2.25rem; |
||||||
|
height: 2.25rem; |
||||||
|
padding: 0.375rem; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
||||||
Loading…
Reference in new issue