Files
shadcn-slint/ui/components/tabs.slint
2026-01-30 12:56:00 +08:00

126 lines
3.4 KiB
Plaintext

import { Theme, SpacingSystem, Typography, Animations } from "../theme/theme.slint";
export struct TabItem {
title: string,
}
export component Tabs {
in property <[TabItem]> tabs: [];
in-out property <int> current-index: 0;
callback tab-changed(int);
min-height: 200px;
VerticalLayout {
spacing: SpacingSystem.spacing.s2;
// Tab List
tab-list := HorizontalLayout {
height: 36px;
spacing: 0;
Rectangle {
background: Theme.colors.muted;
border-radius: SpacingSystem.radius.lg;
padding: 3px;
HorizontalLayout {
spacing: 0;
padding: 0;
for tab[index] in root.tabs: TabTrigger {
text: tab.title;
active: index == root.current-index;
clicked => {
root.current-index = index;
root.tab-changed(index);
}
}
}
}
}
// Tab Content Area
content-area := Rectangle {
// Content will be provided by @children in parent component
@children
}
}
}
component TabTrigger {
in property <string> text: "";
in property <bool> active: false;
callback clicked();
private property <bool> hovered: false;
min-width: 60px;
height: 30px;
states [
active when root.active: {
container.background: Theme.colors.background;
label.color: Theme.colors.foreground;
container.border-color: Theme.colors.input;
}
inactive when !root.active: {
container.background: transparent;
label.color: Theme.colors.muted-foreground;
container.border-color: transparent;
}
hovered when root.hovered && !root.active: {
label.color: Theme.colors.foreground;
}
]
container := Rectangle {
border-radius: SpacingSystem.radius.md;
background: transparent;
border-width: 1px;
border-color: transparent;
drop-shadow-blur: root.active ? 2px : 0;
drop-shadow-color: root.active ? #00000010 : transparent;
drop-shadow-offset-y: root.active ? 1px : 0;
animate background { duration: Animations.durations.fast; }
animate border-color { duration: Animations.durations.fast; }
label := Text {
text: root.text;
color: Theme.colors.muted-foreground;
font-size: Typography.sizes.sm;
font-weight: Typography.weights.medium;
horizontal-alignment: center;
vertical-alignment: center;
animate color { duration: Animations.durations.fast; }
}
}
touch := TouchArea {
clicked => {
root.clicked();
}
moved => {
root.hovered = self.has-hover;
}
}
}
// TabContent component for wrapping content
export component TabContent {
in property <int> index: 0;
in property <int> current-index: 0;
visible: root.index == root.current-index;
VerticalLayout {
@children
}
}