nosignal-shell/modules/detachedcontent/NavRail.qml
2025-07-26 15:45:19 +10:00

198 lines
5.9 KiB
QML

pragma ComponentBehavior: Bound
import qs.widgets
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
Item {
id: root
required property Session session
property bool expanded
implicitWidth: layout.implicitWidth + Appearance.padding.large * 4
implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
ColumnLayout {
id: layout
anchors.centerIn: parent
spacing: Appearance.spacing.normal
states: State {
name: "expanded"
when: root.expanded
PropertyChanges {
layout.spacing: Appearance.spacing.small
menuIcon.opacity: 0
menuIconExpanded.opacity: 1
menuIcon.rotation: 180
menuIconExpanded.rotation: 0
}
AnchorChanges {
target: menuIcon
anchors.horizontalCenter: undefined
}
AnchorChanges {
target: menuIconExpanded
anchors.horizontalCenter: undefined
}
}
transitions: Transition {
Anim {
properties: "spacing,opacity,rotation"
}
}
Item {
Layout.fillWidth: true
Layout.bottomMargin: Appearance.spacing.large * 2
implicitHeight: Math.max(menuIcon.implicitHeight, menuIconExpanded.implicitHeight) + Appearance.padding.normal * 2
StateLayer {
radius: Appearance.rounding.small
function onClicked(): void {
root.expanded = !root.expanded;
}
}
MaterialIcon {
id: menuIcon
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: "menu"
font.pointSize: Appearance.font.size.large
}
MaterialIcon {
id: menuIconExpanded
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: "menu_open"
font.pointSize: Appearance.font.size.large
opacity: 0
rotation: -180
}
}
NavItem {
icon: "network_manage"
label: "network"
}
NavItem {
icon: "settings_bluetooth"
label: "bluetooth"
}
}
component NavItem: Item {
id: item
required property string icon
required property string label
readonly property bool active: root.session.active === label
implicitWidth: background.implicitWidth
implicitHeight: background.implicitHeight + smallLabel.implicitHeight + smallLabel.anchors.topMargin
states: State {
name: "expanded"
when: root.expanded
PropertyChanges {
expandedLabel.opacity: 1
smallLabel.opacity: 0
background.implicitWidth: Config.dcontent.sizes.expandedNavWidth
background.implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2
item.implicitHeight: background.implicitHeight
}
}
transitions: Transition {
Anim {
property: "opacity"
duration: Appearance.anim.durations.small
}
Anim {
properties: "implicitWidth,implicitHeight"
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
StyledRect {
id: background
radius: Appearance.rounding.full
color: item.active ? Colours.palette.m3secondaryContainer : Colours.palette.m3surfaceContainer
implicitWidth: icon.implicitWidth + icon.anchors.leftMargin * 2
implicitHeight: icon.implicitHeight + Appearance.padding.small
StateLayer {
color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
function onClicked(): void {
root.session.active = item.label;
}
}
MaterialIcon {
id: icon
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Appearance.padding.large
text: item.icon
color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
font.pointSize: Appearance.font.size.large
fill: item.active ? 1 : 0
Behavior on fill {
Anim {}
}
}
StyledText {
id: expandedLabel
anchors.left: icon.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Appearance.spacing.small
opacity: 0
text: item.label
color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
font.capitalization: Font.Capitalize
}
StyledText {
id: smallLabel
anchors.horizontalCenter: icon.horizontalCenter
anchors.top: icon.bottom
anchors.topMargin: Appearance.spacing.small / 2
text: item.label
font.pointSize: Appearance.font.size.small
font.capitalization: Font.Capitalize
}
}
}
component Anim: NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}