Files
Website/src/lib/components/tui/TuiGroup.svelte
2025-12-01 06:55:28 +00:00

130 lines
2.8 KiB
Svelte

<script lang="ts">
import { parseColorText, getPlainText } from "./utils";
import { themeColors } from "$lib/stores/theme";
import TuiLine from "./TuiLine.svelte";
import type { GroupLine, TerminalLine } from "./types";
interface Props {
line: GroupLine;
inline?: boolean;
onButtonClick?: (idx: number, line?: TerminalLine) => void;
onHoverButton?: (idx: number) => void;
onLinkClick?: (idx: number) => void;
}
let {
line,
inline = false,
onButtonClick = () => {},
onHoverButton = () => {},
onLinkClick = () => {},
}: Props = $props();
// Get colorMap from current theme
const colorMap = $derived($themeColors.colorMap);
// Parse children with current colorMap
const parsedChildren = $derived(
(line.children || []).map((child) => {
const segments = parseColorText(child.content, colorMap);
return { line: child, segments, plainText: getPlainText(segments) };
}),
);
// Style for the group container
const groupStyle = $derived(() => {
const styles: string[] = [];
// Only apply flex styles if not grid or block
if (line.display !== "grid" && line.display !== "block") {
if (line.groupDirection === "column")
styles.push("flex-direction: column");
if (line.groupAlign) {
const alignMap: Record<string, string> = {
start: "flex-start",
center: "center",
end: "flex-end",
stretch: "stretch",
};
styles.push(
`align-items: ${alignMap[line.groupAlign] || "flex-start"}`,
);
}
}
if (line.groupGap) styles.push(`gap: ${line.groupGap}`);
return styles.join("; ");
});
</script>
<div
class="tui-group"
class:inline
class:grid={line.display === "grid"}
class:block={line.display === "block"}
class:expand={line.groupExpand}
style={groupStyle()}
id={line.id}
>
{#each parsedChildren as parsed, idx (idx)}
<TuiLine
line={parsed.line}
index={idx}
segments={parsed.segments}
complete={true}
showImage={parsed.line.type === "image"}
selectedIndex={-1}
inline={parsed.line.inline !== false}
{onButtonClick}
{onHoverButton}
{onLinkClick}
/>
{/each}
</div>
<style>
.tui-group {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 0.75rem;
animation: lineSlideIn 0.15s ease-out;
}
.tui-group.inline {
display: inline-flex;
}
.tui-group.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
width: 100%;
}
.tui-group.block {
display: block;
width: 100%;
}
.tui-group.expand > :global(*) {
flex: 1;
}
@keyframes lineSlideIn {
from {
opacity: 0;
transform: translateX(-5px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@media (max-width: 768px) {
/* .tui-group.inline removed to allow inline flow on mobile */
.tui-group.grid {
grid-template-columns: 1fr;
}
}
</style>