#!/usr/bin/env python # -*- coding: utf-8 -*- """ Flet‑приложение: боковая панель + рабочая область (с учётом всех уточнений: исчезновение текста при сворачивании, акцент только на активной группе/вкладке, иконка‑кнопка смены темы, фиолетовый акцентный цвет). """ import flet as ft # ---------- Параметры изображений ---------- LOGO_LIGHT_COLLAPSED = "logo_light_small.png" # светлый логотип, «маленький» LOGO_LIGHT_EXPANDED = "logo_light_large.png" # светлый логотип, «большой» LOGO_DARK_COLLAPSED = "logo_dark_small.png" # тёмный логотип, «маленький» LOGO_DARK_EXPANDED = "logo_dark_large.png" # тёмный логотип, «большой» # ---------- Цвета ---------- LIGHT_BG = "#e0f7fa" DARK_BG = "#263238" LIGHT_ACC = "#9C27B0" # фиолетовый 500 DARK_ACC = "#7B1FA2" # фиолетовый 700 # ---------- Вспомогательные функции ---------- def status_lamp(color: str) -> ft.Icon: return ft.Icon(ft.Icons.CIRCLE, size=12, color=color) def group_icon(name: str) -> ft.Icon: mapping = { "Repository": ft.Icons.ARCHIVE, "Environment": ft.Icons.FOLDER, "Civit": ft.Icons.HEXAGON, "Tasks": ft.Icons.DOWNLOAD, } return ft.Icon(mapping.get(name, ft.Icons.FOLDER), size=20) def tab_icon() -> ft.Icon: return ft.Icon(ft.Icons.FILE_COPY, size=30) # ---------- Главная функция ---------- def main(page: ft.Page): page.title = "Flet Sidebar Demo" page.vertical_alignment = ft.MainAxisAlignment.START page.horizontal_alignment = ft.CrossAxisAlignment.START # ----- Состояния ----- is_expanded = True group_expanded = { "Repository": True, "Environment": False, "Civit": False, "Tasks": False, } selected_group: str | None = None selected_tab_name: str | None = None selected_tab = ft.Text(value="Выберите вкладку", size=24, weight=ft.FontWeight.W_400) # ----- Логотип ----- def get_logo_path() -> str: if page.theme_mode == ft.ThemeMode.LIGHT: return LOGO_LIGHT_EXPANDED if is_expanded else LOGO_LIGHT_COLLAPSED else: return LOGO_DARK_EXPANDED if is_expanded else LOGO_DARK_COLLAPSED # ----- Панель навигации ----- sidebar = ft.Container( width=200 if is_expanded else 60, bgcolor=ft.Colors.SURFACE, padding=ft.padding.all(8), content=ft.Column(spacing=8, controls=[]), ) # ----- Рабочая область ----- main_area = ft.Container( expand=True, padding=ft.padding.all(20), content=ft.Container( alignment=ft.alignment.center, content=selected_tab ) ) # ----- Макет ----- page.add(ft.Row(controls=[sidebar, main_area], expand=True)) # ----- Пересоздание боковой панели ----- def rebuild_sidebar(): """Пересоздаёт содержимое боковой панели.""" controls = [] # 1. Логотип (с кликом) logo_img = ft.Image( src=get_logo_path(), width=50 if not is_expanded else 150, height=50 if not is_expanded else 150, fit=ft.ImageFit.CONTAIN ) logo_container = ft.Container( content=logo_img, on_click=lambda e: toggle_sidebar() ) controls.append(logo_container) # 2. Группы вкладок groups = { "Repository": ["Create", "Upload"], "Environment": ["Create", "Upload", "Install"], "Civit": ["Initialize", "Overview", "Selection"], "Tasks": ["Upload"], } for grp_name, tabs in groups.items(): controls.append(build_group(grp_name, tabs)) # 3. Кнопка смены темы (только иконка) controls.append(ft.Container(height=20)) theme_icon = ft.Icons.SUNNY if page.theme_mode == ft.ThemeMode.LIGHT else ft.Icons.NIGHTLIGHT_ROUNDED theme_btn = ft.IconButton( icon=theme_icon, on_click=lambda e: toggle_theme() ) controls.append(theme_btn) sidebar.content.controls = controls page.update() # ----- Группа + подменю ----- def build_group(name: str, tabs: list[str]) -> ft.Container: """Создаёт одну группу с подменю.""" # Фон заголовка – только для активной группы header_bg = LIGHT_ACC if selected_group == name else ft.Colors.TRANSPARENT # 1️⃣ Первый ряд: статус‑лампочка, иконка, название группы title_row = ft.Row( controls=[ status_lamp("#ffeb3b"), group_icon(name), ft.Text(name, weight=ft.FontWeight.BOLD, color=ft.Colors.ON_PRIMARY) ], alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER, spacing=8, ) # 2️⃣ Второй ряд: подстрока (отображается только при развёрнутой панели) subtitle_row = ft.Row( controls=[ ft.Text("Подстрока", size=10, color=ft.Colors.GREY) ], alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER, spacing=8, ) header_content = ft.Column( controls=[title_row] + ([subtitle_row] if is_expanded else []), spacing=2, ) header = ft.Container( padding=ft.padding.only(left=8, right=8, top=4, bottom=4), bgcolor=header_bg, border_radius=8, content=header_content, on_click=lambda e: toggle_group(name), ) # Список вкладок tab_items = [] for tab_name in tabs: icon = tab_icon() title = ft.Text(tab_name, color=ft.Colors.ON_SURFACE_VARIANT) if selected_tab_name == tab_name: icon.color = LIGHT_ACC if page.theme_mode == ft.ThemeMode.LIGHT else DARK_ACC title.color = LIGHT_ACC if page.theme_mode == ft.ThemeMode.LIGHT else DARK_ACC row = ft.Row( controls=[icon], alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER, spacing=8 ) if is_expanded: row.controls.append(title) item = ft.Container( content=row, padding=ft.padding.only(left=16), on_click=lambda e, t=tab_name, g=name: select_tab(g, t) ) tab_items.append(item) sublist = ft.Container( content=ft.Column(controls=tab_items, spacing=0), height=0 if not group_expanded[name] else len(tabs) * 48, ) return ft.Column( controls=[header, sublist], spacing=4 ) # ----- События ----- def toggle_sidebar(): nonlocal is_expanded is_expanded = not is_expanded sidebar.width = 200 if is_expanded else 80 rebuild_sidebar() def toggle_group(name: str): group_expanded[name] = not group_expanded[name] rebuild_sidebar() def select_tab(group: str, tab: str): nonlocal selected_group, selected_tab_name selected_group = group selected_tab_name = tab selected_tab.value = f"{tab}\n(Icon + text)" rebuild_sidebar() def toggle_theme(): if page.theme_mode == ft.ThemeMode.LIGHT: page.theme_mode = ft.ThemeMode.DARK page.bgcolor = DARK_BG else: page.theme_mode = ft.ThemeMode.LIGHT page.bgcolor = LIGHT_BG rebuild_sidebar() page.update() # ----- Инициализация ----- page.bgcolor = LIGHT_BG rebuild_sidebar() # ---------- Запуск ---------- if __name__ == "__main__": ft.app(target=main)