-
This commit is contained in:
125
ui/components/tabs.slint
Normal file
125
ui/components/tabs.slint
Normal file
@@ -0,0 +1,125 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user