Files
website/src/lib/components/tui/TuiCard.svelte
T

182 lines
3.5 KiB
Svelte

<script lang="ts">
import Icon from "@iconify/svelte";
import { getButtonStyle, parseColorText, getSegmentStyle } from "./utils";
import type { CardLine } from "./types";
import TuiLine from "./TuiLine.svelte";
import "$lib/assets/css/tui-card.css";
interface Props {
line: CardLine;
inline?: boolean;
}
let { line, inline = false }: Props = $props();
const segments = $derived(parseColorText(line.content));
const titleSegments = $derived(
line.cardTitle ? parseColorText(line.cardTitle) : [],
);
const footerSegments = $derived(
line.cardFooter ? parseColorText(line.cardFooter) : [],
);
</script>
<div
class="tui-card"
class:inline
style="--card-color: {getButtonStyle(line.style)}"
>
{#if line.cardTitle}
<div class="card-header">
{#if line.icon}
<Icon icon={line.icon} width="16" class="card-icon" />
{/if}
<span class="card-title">
{#each titleSegments as segment}
{#if getSegmentStyle(segment)}
<span style={getSegmentStyle(segment)}
>{segment.text}</span
>
{:else}
{segment.text}
{/if}
{/each}
</span>
</div>
{/if}
<div class="card-body">
{#if line.image}
<img
src={line.image}
alt={line.imageAlt || ""}
class="card-image"
/>
{/if}
<div class="card-content">
{#each segments as segment}
{#if getSegmentStyle(segment)}
<span style={getSegmentStyle(segment)}>{segment.text}</span>
{:else}
{segment.text}
{/if}
{/each}
</div>
{#if line.children && line.children.length > 0}
<div
class="card-children"
class:flex={line.display === "flex"}
class:grid={line.display === "grid"}
>
{#each line.children as child, i}
<TuiLine
line={child}
index={i}
segments={parseColorText(child.content)}
complete={true}
showImage={true}
selectedIndex={-1}
inline={false}
/>
{/each}
</div>
{/if}
</div>
{#if line.cardFooter}
<div class="card-footer">
{#each footerSegments as segment}
{#if getSegmentStyle(segment)}
<span style={getSegmentStyle(segment)}>{segment.text}</span>
{:else}
{segment.text}
{/if}
{/each}
</div>
{/if}
</div>
<style>
.tui-card {
border: 1px solid var(--terminal-border);
border-radius: 6px;
background: color-mix(
in srgb,
var(--terminal-bg) 80%,
var(--card-accent) 5%
);
margin: 0.5rem 0;
overflow: hidden;
transition: border-color 0.2s ease;
}
.tui-card:hover {
border-color: var(--card-accent);
}
.card-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 0.75rem;
border-bottom: 1px solid var(--terminal-border);
background: rgba(0, 0, 0, 0.1);
}
:global(.card-icon) {
color: var(--card-accent);
}
.card-title {
font-weight: 600;
color: var(--terminal-text);
font-size: 0.85rem;
}
.card-body {
padding: 0.75rem;
}
.card-image {
width: 100%;
max-height: 200px;
object-fit: cover;
border-radius: 4px;
margin-bottom: 0.5rem;
}
.card-content {
color: var(--terminal-muted);
font-size: 0.85rem;
line-height: 1.5;
white-space: pre-wrap;
}
.card-children {
margin-top: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.card-children.flex {
flex-direction: row;
flex-wrap: wrap;
gap: 1rem;
}
.card-children.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.card-footer {
padding: 0.5rem 0.75rem;
border-top: 1px solid var(--terminal-border);
font-size: 0.75rem;
color: var(--terminal-muted);
background: rgba(0, 0, 0, 0.05);
}
</style>