fletplus 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fletplus-0.1.0/LICENSE.md +21 -0
- fletplus-0.1.0/PKG-INFO +100 -0
- fletplus-0.1.0/README.md +80 -0
- fletplus-0.1.0/fletplus/__init__.py +0 -0
- fletplus-0.1.0/fletplus/components/__init__.py +0 -0
- fletplus-0.1.0/fletplus/components/responsive_grid.py +37 -0
- fletplus-0.1.0/fletplus/components/sidebar_admin.py +52 -0
- fletplus-0.1.0/fletplus/components/smart_table.py +70 -0
- fletplus-0.1.0/fletplus/core.py +54 -0
- fletplus-0.1.0/fletplus/themes/__init__.py +0 -0
- fletplus-0.1.0/fletplus/themes/theme_manager.py +25 -0
- fletplus-0.1.0/fletplus/utils/__init__.py +0 -0
- fletplus-0.1.0/fletplus/utils/responsive_manager.py +0 -0
- fletplus-0.1.0/fletplus.egg-info/PKG-INFO +100 -0
- fletplus-0.1.0/fletplus.egg-info/SOURCES.txt +25 -0
- fletplus-0.1.0/fletplus.egg-info/dependency_links.txt +1 -0
- fletplus-0.1.0/fletplus.egg-info/requires.txt +1 -0
- fletplus-0.1.0/fletplus.egg-info/top_level.txt +1 -0
- fletplus-0.1.0/pyproject.toml +23 -0
- fletplus-0.1.0/setup.cfg +4 -0
- fletplus-0.1.0/setup.py +30 -0
- fletplus-0.1.0/tests/test_all.py +14 -0
- fletplus-0.1.0/tests/test_fletplus_app.py +58 -0
- fletplus-0.1.0/tests/test_responsive_grid.py +29 -0
- fletplus-0.1.0/tests/test_sidebar_admin.py +38 -0
- fletplus-0.1.0/tests/test_smart_table.py +48 -0
- fletplus-0.1.0/tests/test_theme_manager.py +33 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Adolfo González
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the “Software”), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
fletplus-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fletplus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Componentes visuales avanzados para Flet en Python
|
|
5
|
+
Home-page: https://github.com/tu_usuario/fletplus
|
|
6
|
+
Author: Adolfo González Hernández
|
|
7
|
+
Author-email: Adolfo González <adolfogonzal@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/Alphonsus411/fletplus
|
|
10
|
+
Project-URL: Repository, https://github.com/Alphonsus411/fletplus
|
|
11
|
+
Project-URL: Documentation, https://github.com/Alphonsus411/fletplus#readme
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE.md
|
|
15
|
+
Requires-Dist: flet>=0.27.0
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
|
|
21
|
+
# 🚀 FletPlus
|
|
22
|
+
|
|
23
|
+
**FletPlus** es una librería de componentes visuales y utilidades para acelerar el desarrollo de interfaces modernas en Python usando [Flet](https://flet.dev).
|
|
24
|
+
Proporciona un conjunto de controles personalizables como tablas inteligentes, grillas responsivas, barras laterales, gestores de tema y estructura modular de apps.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 📦 Instalación
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install fletplus
|
|
32
|
+
```
|
|
33
|
+
- **Requiere Python 3.9+ y flet>=0.27.0**
|
|
34
|
+
|
|
35
|
+
## 🧩 Componentes incluidos
|
|
36
|
+
|
|
37
|
+
| Componente | Descripción |
|
|
38
|
+
|----------------|---------------------------------------------------|
|
|
39
|
+
| `SmartTable` | Tabla con paginación y ordenamiento integrados |
|
|
40
|
+
| `SidebarAdmin` | Menú lateral dinámico con ítems y selección |
|
|
41
|
+
| `ResponsiveGrid` | Distribución de contenido adaptable a pantalla |
|
|
42
|
+
| `ThemeManager` | Gestión centralizada de modo claro/oscuro |
|
|
43
|
+
| `FletPlusApp` | Estructura base para apps con navegación y tema |
|
|
44
|
+
|
|
45
|
+
# 🧪 Ejemplo rápido
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import flet as ft
|
|
49
|
+
from fletplus.components.smart_table import SmartTable
|
|
50
|
+
|
|
51
|
+
def main(page: ft.Page):
|
|
52
|
+
rows = [
|
|
53
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("1")), ft.DataCell(ft.Text("Alice"))]),
|
|
54
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("2")), ft.DataCell(ft.Text("Bob"))]),
|
|
55
|
+
]
|
|
56
|
+
table = SmartTable(["ID", "Nombre"], rows)
|
|
57
|
+
page.add(table.build())
|
|
58
|
+
|
|
59
|
+
ft.app(target=main)
|
|
60
|
+
```
|
|
61
|
+
# 🔧 Estructura del proyecto
|
|
62
|
+
|
|
63
|
+
fletplus/
|
|
64
|
+
├── components/
|
|
65
|
+
│ ├── smart_table.py
|
|
66
|
+
│ ├── sidebar_admin.py
|
|
67
|
+
│ └── responsive_grid.py
|
|
68
|
+
├── themes/
|
|
69
|
+
│ └── theme_manager.py
|
|
70
|
+
├── core.py ← Clase FletPlusApp
|
|
71
|
+
|
|
72
|
+
# 📋 Tests
|
|
73
|
+
|
|
74
|
+
Todos los componentes están cubiertos por tests unitarios (ver carpeta tests/).
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pytest --cov=fletplus
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
# 🛠️ Contribuir
|
|
81
|
+
|
|
82
|
+
Las contribuciones son bienvenidas:
|
|
83
|
+
|
|
84
|
+
1. **Haz un fork**
|
|
85
|
+
|
|
86
|
+
2. **Crea tu rama**: git checkout -b feature/nueva-funcionalidad
|
|
87
|
+
|
|
88
|
+
3. **Abre un PR** explicando el cambio
|
|
89
|
+
|
|
90
|
+
# 📄 Licencia
|
|
91
|
+
|
|
92
|
+
MIT License
|
|
93
|
+
|
|
94
|
+
Copyright (c) 2025 Adolfo González
|
|
95
|
+
|
|
96
|
+
# 💬 Contacto
|
|
97
|
+
|
|
98
|
+
Desarrollado por Adolfo González Hernández.
|
|
99
|
+
|
|
100
|
+
**email**: adolfogonzal@gmail.com
|
fletplus-0.1.0/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# 🚀 FletPlus
|
|
2
|
+
|
|
3
|
+
**FletPlus** es una librería de componentes visuales y utilidades para acelerar el desarrollo de interfaces modernas en Python usando [Flet](https://flet.dev).
|
|
4
|
+
Proporciona un conjunto de controles personalizables como tablas inteligentes, grillas responsivas, barras laterales, gestores de tema y estructura modular de apps.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 📦 Instalación
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install fletplus
|
|
12
|
+
```
|
|
13
|
+
- **Requiere Python 3.9+ y flet>=0.27.0**
|
|
14
|
+
|
|
15
|
+
## 🧩 Componentes incluidos
|
|
16
|
+
|
|
17
|
+
| Componente | Descripción |
|
|
18
|
+
|----------------|---------------------------------------------------|
|
|
19
|
+
| `SmartTable` | Tabla con paginación y ordenamiento integrados |
|
|
20
|
+
| `SidebarAdmin` | Menú lateral dinámico con ítems y selección |
|
|
21
|
+
| `ResponsiveGrid` | Distribución de contenido adaptable a pantalla |
|
|
22
|
+
| `ThemeManager` | Gestión centralizada de modo claro/oscuro |
|
|
23
|
+
| `FletPlusApp` | Estructura base para apps con navegación y tema |
|
|
24
|
+
|
|
25
|
+
# 🧪 Ejemplo rápido
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import flet as ft
|
|
29
|
+
from fletplus.components.smart_table import SmartTable
|
|
30
|
+
|
|
31
|
+
def main(page: ft.Page):
|
|
32
|
+
rows = [
|
|
33
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("1")), ft.DataCell(ft.Text("Alice"))]),
|
|
34
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("2")), ft.DataCell(ft.Text("Bob"))]),
|
|
35
|
+
]
|
|
36
|
+
table = SmartTable(["ID", "Nombre"], rows)
|
|
37
|
+
page.add(table.build())
|
|
38
|
+
|
|
39
|
+
ft.app(target=main)
|
|
40
|
+
```
|
|
41
|
+
# 🔧 Estructura del proyecto
|
|
42
|
+
|
|
43
|
+
fletplus/
|
|
44
|
+
├── components/
|
|
45
|
+
│ ├── smart_table.py
|
|
46
|
+
│ ├── sidebar_admin.py
|
|
47
|
+
│ └── responsive_grid.py
|
|
48
|
+
├── themes/
|
|
49
|
+
│ └── theme_manager.py
|
|
50
|
+
├── core.py ← Clase FletPlusApp
|
|
51
|
+
|
|
52
|
+
# 📋 Tests
|
|
53
|
+
|
|
54
|
+
Todos los componentes están cubiertos por tests unitarios (ver carpeta tests/).
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pytest --cov=fletplus
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
# 🛠️ Contribuir
|
|
61
|
+
|
|
62
|
+
Las contribuciones son bienvenidas:
|
|
63
|
+
|
|
64
|
+
1. **Haz un fork**
|
|
65
|
+
|
|
66
|
+
2. **Crea tu rama**: git checkout -b feature/nueva-funcionalidad
|
|
67
|
+
|
|
68
|
+
3. **Abre un PR** explicando el cambio
|
|
69
|
+
|
|
70
|
+
# 📄 Licencia
|
|
71
|
+
|
|
72
|
+
MIT License
|
|
73
|
+
|
|
74
|
+
Copyright (c) 2025 Adolfo González
|
|
75
|
+
|
|
76
|
+
# 💬 Contacto
|
|
77
|
+
|
|
78
|
+
Desarrollado por Adolfo González Hernández.
|
|
79
|
+
|
|
80
|
+
**email**: adolfogonzal@gmail.com
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
|
|
3
|
+
class ResponsiveGrid:
|
|
4
|
+
def __init__(self, children: list[ft.Control], breakpoints=None, spacing=10):
|
|
5
|
+
"""
|
|
6
|
+
:param children: Lista de widgets (flet.Control) a distribuir en la grilla.
|
|
7
|
+
:param breakpoints: Diccionario {ancho_px: columnas}, por ejemplo {0: 1, 600: 2, 900: 3}
|
|
8
|
+
:param spacing: Espaciado entre elementos
|
|
9
|
+
"""
|
|
10
|
+
self.children = children
|
|
11
|
+
self.spacing = spacing
|
|
12
|
+
self.breakpoints = breakpoints or {
|
|
13
|
+
0: 1,
|
|
14
|
+
600: 2,
|
|
15
|
+
900: 3,
|
|
16
|
+
1200: 4
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def build(self, page_width: int):
|
|
20
|
+
# Determinar cuántas columnas según el ancho de página
|
|
21
|
+
columns = 1
|
|
22
|
+
for bp, cols in sorted(self.breakpoints.items()):
|
|
23
|
+
if page_width >= bp:
|
|
24
|
+
columns = cols
|
|
25
|
+
|
|
26
|
+
col_span = max(1, int(12 / columns)) # Sistema de 12 columnas de Flet
|
|
27
|
+
|
|
28
|
+
return ft.ResponsiveRow(
|
|
29
|
+
controls=[
|
|
30
|
+
ft.Container(
|
|
31
|
+
content=child,
|
|
32
|
+
col=col_span,
|
|
33
|
+
padding=self.spacing
|
|
34
|
+
) for child in self.children
|
|
35
|
+
],
|
|
36
|
+
alignment=ft.MainAxisAlignment.START
|
|
37
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# fletplus/components/sidebar_admin.py
|
|
2
|
+
|
|
3
|
+
import flet as ft
|
|
4
|
+
|
|
5
|
+
class SidebarAdmin:
|
|
6
|
+
def __init__(self, menu_items, on_select=None, header="Menú", width=250):
|
|
7
|
+
"""
|
|
8
|
+
:param menu_items: Lista de dicts con {"title": str, "icon": ft.IconName}
|
|
9
|
+
:param on_select: Función callback cuando se selecciona un ítem
|
|
10
|
+
:param header: Título de la barra lateral
|
|
11
|
+
:param width: Anchura del sidebar
|
|
12
|
+
"""
|
|
13
|
+
self.menu_items = menu_items
|
|
14
|
+
self.on_select = on_select
|
|
15
|
+
self.header = header
|
|
16
|
+
self.width = width
|
|
17
|
+
self.selected_index = 0
|
|
18
|
+
self.tiles = [] # Para actualizar visualmente los seleccionados
|
|
19
|
+
|
|
20
|
+
def build(self):
|
|
21
|
+
self.tiles = []
|
|
22
|
+
|
|
23
|
+
for i, item in enumerate(self.menu_items):
|
|
24
|
+
tile = ft.ListTile(
|
|
25
|
+
title=ft.Text(item["title"]),
|
|
26
|
+
leading=ft.Icon(item.get("icon", ft.icons.CIRCLE)),
|
|
27
|
+
selected=(i == self.selected_index),
|
|
28
|
+
on_click=lambda e, idx=i: self._select_item(idx, e),
|
|
29
|
+
)
|
|
30
|
+
self.tiles.append(tile)
|
|
31
|
+
|
|
32
|
+
return ft.Container(
|
|
33
|
+
width=self.width,
|
|
34
|
+
bgcolor=ft.colors.SURFACE_VARIANT,
|
|
35
|
+
padding=10,
|
|
36
|
+
content=ft.Column([
|
|
37
|
+
ft.Text(self.header, size=20, weight="bold"),
|
|
38
|
+
ft.Divider(),
|
|
39
|
+
ft.Column(self.tiles, expand=True),
|
|
40
|
+
])
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def _select_item(self, index, e):
|
|
44
|
+
self.selected_index = index
|
|
45
|
+
|
|
46
|
+
for i, tile in enumerate(self.tiles):
|
|
47
|
+
tile.selected = (i == index)
|
|
48
|
+
|
|
49
|
+
if self.on_select:
|
|
50
|
+
self.on_select(index)
|
|
51
|
+
|
|
52
|
+
e.control.page.update()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
|
|
3
|
+
class SmartTable:
|
|
4
|
+
def __init__(self, columns, rows, sortable=True, page_size=10):
|
|
5
|
+
self.columns = columns
|
|
6
|
+
self.rows = rows
|
|
7
|
+
self.sortable = sortable
|
|
8
|
+
self.page_size = page_size
|
|
9
|
+
self.current_page = 0
|
|
10
|
+
self.sorted_column = None
|
|
11
|
+
self.sort_ascending = True
|
|
12
|
+
|
|
13
|
+
def build(self):
|
|
14
|
+
return ft.Column([
|
|
15
|
+
ft.DataTable(
|
|
16
|
+
columns=[
|
|
17
|
+
ft.DataColumn(
|
|
18
|
+
label=ft.Text(col),
|
|
19
|
+
on_sort=self._on_sort(index) if self.sortable else None
|
|
20
|
+
) for index, col in enumerate(self.columns)
|
|
21
|
+
],
|
|
22
|
+
rows=self._get_page_rows()
|
|
23
|
+
),
|
|
24
|
+
ft.Row([
|
|
25
|
+
ft.ElevatedButton("Anterior", on_click=self._previous_page),
|
|
26
|
+
ft.ElevatedButton("Siguiente", on_click=self._next_page),
|
|
27
|
+
])
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
def _on_sort(self, col_index):
|
|
31
|
+
def handler(e):
|
|
32
|
+
if self.sorted_column == col_index:
|
|
33
|
+
self.sort_ascending = not self.sort_ascending
|
|
34
|
+
else:
|
|
35
|
+
self.sorted_column = col_index
|
|
36
|
+
self.sort_ascending = True
|
|
37
|
+
|
|
38
|
+
self.rows.sort(
|
|
39
|
+
key=lambda x: x.cells[col_index].content.value,
|
|
40
|
+
reverse=not self.sort_ascending
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Intentar actualizar la página si existe
|
|
44
|
+
try:
|
|
45
|
+
e.control.page.update()
|
|
46
|
+
except AttributeError:
|
|
47
|
+
pass # Permite testear sin page real
|
|
48
|
+
|
|
49
|
+
return handler
|
|
50
|
+
|
|
51
|
+
def _get_page_rows(self):
|
|
52
|
+
start = self.current_page * self.page_size
|
|
53
|
+
end = start + self.page_size
|
|
54
|
+
return self.rows[start:end]
|
|
55
|
+
|
|
56
|
+
def _next_page(self, e):
|
|
57
|
+
if (self.current_page + 1) * self.page_size < len(self.rows):
|
|
58
|
+
self.current_page += 1
|
|
59
|
+
try:
|
|
60
|
+
e.control.page.update()
|
|
61
|
+
except AttributeError:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
def _previous_page(self, e):
|
|
65
|
+
if self.current_page > 0:
|
|
66
|
+
self.current_page -= 1
|
|
67
|
+
try:
|
|
68
|
+
e.control.page.update()
|
|
69
|
+
except AttributeError:
|
|
70
|
+
pass
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
from fletplus.themes.theme_manager import ThemeManager
|
|
3
|
+
from fletplus.components.sidebar_admin import SidebarAdmin
|
|
4
|
+
|
|
5
|
+
class FletPlusApp:
|
|
6
|
+
def __init__(self, page: ft.Page, routes: dict, sidebar_items=None, title="FletPlus App", theme_config=None):
|
|
7
|
+
"""
|
|
8
|
+
:param page: Página Flet actual
|
|
9
|
+
:param routes: Diccionario de rutas {str: Callable}
|
|
10
|
+
:param sidebar_items: Lista de ítems del sidebar [{title, icon}]
|
|
11
|
+
:param title: Título de la app
|
|
12
|
+
:param theme_config: Diccionario de configuración inicial del tema
|
|
13
|
+
"""
|
|
14
|
+
self.page = page
|
|
15
|
+
self.routes = routes
|
|
16
|
+
self.sidebar_items = sidebar_items or []
|
|
17
|
+
self.theme = ThemeManager(page, **(theme_config or {}))
|
|
18
|
+
self.title = title
|
|
19
|
+
|
|
20
|
+
self.content_container = ft.Container(expand=True, bgcolor=ft.colors.BACKGROUND)
|
|
21
|
+
self.sidebar = SidebarAdmin(self.sidebar_items, on_select=self._on_nav)
|
|
22
|
+
|
|
23
|
+
def build(self):
|
|
24
|
+
self.page.title = self.title
|
|
25
|
+
self.page.horizontal_alignment = ft.CrossAxisAlignment.START
|
|
26
|
+
self.page.scroll = "auto"
|
|
27
|
+
|
|
28
|
+
self.theme.apply_theme()
|
|
29
|
+
|
|
30
|
+
# Mostrar primer contenido
|
|
31
|
+
self._load_route(0)
|
|
32
|
+
|
|
33
|
+
self.page.add(
|
|
34
|
+
ft.Row([
|
|
35
|
+
self.sidebar.build(),
|
|
36
|
+
self.content_container
|
|
37
|
+
])
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def _on_nav(self, index):
|
|
41
|
+
self._load_route(index)
|
|
42
|
+
|
|
43
|
+
def _load_route(self, index):
|
|
44
|
+
route_key = list(self.routes.keys())[index]
|
|
45
|
+
builder = self.routes[route_key]
|
|
46
|
+
self.content_container.content = builder()
|
|
47
|
+
self.page.update()
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def start(cls, routes, sidebar_items=None, title="FletPlus App", theme_config=None):
|
|
51
|
+
def main(page: ft.Page):
|
|
52
|
+
app = cls(page, routes, sidebar_items, title, theme_config)
|
|
53
|
+
app.build()
|
|
54
|
+
ft.app(target=main)
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# fletplus/themes/theme_manager.py
|
|
2
|
+
|
|
3
|
+
import flet as ft
|
|
4
|
+
|
|
5
|
+
class ThemeManager:
|
|
6
|
+
def __init__(self, page: ft.Page, primary_color=ft.colors.BLUE):
|
|
7
|
+
self.page = page
|
|
8
|
+
self.primary_color = primary_color
|
|
9
|
+
self.dark_mode = False
|
|
10
|
+
|
|
11
|
+
def apply_theme(self):
|
|
12
|
+
self.page.theme = ft.Theme(
|
|
13
|
+
color_scheme_seed=self.primary_color
|
|
14
|
+
)
|
|
15
|
+
self.page.theme_mode = ft.ThemeMode.DARK if self.dark_mode else ft.ThemeMode.LIGHT
|
|
16
|
+
self.page.update()
|
|
17
|
+
|
|
18
|
+
def toggle_dark_mode(self):
|
|
19
|
+
self.dark_mode = not self.dark_mode
|
|
20
|
+
self.apply_theme()
|
|
21
|
+
|
|
22
|
+
def set_primary_color(self, color):
|
|
23
|
+
self.primary_color = color
|
|
24
|
+
self.apply_theme()
|
|
25
|
+
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fletplus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Componentes visuales avanzados para Flet en Python
|
|
5
|
+
Home-page: https://github.com/tu_usuario/fletplus
|
|
6
|
+
Author: Adolfo González Hernández
|
|
7
|
+
Author-email: Adolfo González <adolfogonzal@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/Alphonsus411/fletplus
|
|
10
|
+
Project-URL: Repository, https://github.com/Alphonsus411/fletplus
|
|
11
|
+
Project-URL: Documentation, https://github.com/Alphonsus411/fletplus#readme
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE.md
|
|
15
|
+
Requires-Dist: flet>=0.27.0
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
|
|
21
|
+
# 🚀 FletPlus
|
|
22
|
+
|
|
23
|
+
**FletPlus** es una librería de componentes visuales y utilidades para acelerar el desarrollo de interfaces modernas en Python usando [Flet](https://flet.dev).
|
|
24
|
+
Proporciona un conjunto de controles personalizables como tablas inteligentes, grillas responsivas, barras laterales, gestores de tema y estructura modular de apps.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 📦 Instalación
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install fletplus
|
|
32
|
+
```
|
|
33
|
+
- **Requiere Python 3.9+ y flet>=0.27.0**
|
|
34
|
+
|
|
35
|
+
## 🧩 Componentes incluidos
|
|
36
|
+
|
|
37
|
+
| Componente | Descripción |
|
|
38
|
+
|----------------|---------------------------------------------------|
|
|
39
|
+
| `SmartTable` | Tabla con paginación y ordenamiento integrados |
|
|
40
|
+
| `SidebarAdmin` | Menú lateral dinámico con ítems y selección |
|
|
41
|
+
| `ResponsiveGrid` | Distribución de contenido adaptable a pantalla |
|
|
42
|
+
| `ThemeManager` | Gestión centralizada de modo claro/oscuro |
|
|
43
|
+
| `FletPlusApp` | Estructura base para apps con navegación y tema |
|
|
44
|
+
|
|
45
|
+
# 🧪 Ejemplo rápido
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import flet as ft
|
|
49
|
+
from fletplus.components.smart_table import SmartTable
|
|
50
|
+
|
|
51
|
+
def main(page: ft.Page):
|
|
52
|
+
rows = [
|
|
53
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("1")), ft.DataCell(ft.Text("Alice"))]),
|
|
54
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("2")), ft.DataCell(ft.Text("Bob"))]),
|
|
55
|
+
]
|
|
56
|
+
table = SmartTable(["ID", "Nombre"], rows)
|
|
57
|
+
page.add(table.build())
|
|
58
|
+
|
|
59
|
+
ft.app(target=main)
|
|
60
|
+
```
|
|
61
|
+
# 🔧 Estructura del proyecto
|
|
62
|
+
|
|
63
|
+
fletplus/
|
|
64
|
+
├── components/
|
|
65
|
+
│ ├── smart_table.py
|
|
66
|
+
│ ├── sidebar_admin.py
|
|
67
|
+
│ └── responsive_grid.py
|
|
68
|
+
├── themes/
|
|
69
|
+
│ └── theme_manager.py
|
|
70
|
+
├── core.py ← Clase FletPlusApp
|
|
71
|
+
|
|
72
|
+
# 📋 Tests
|
|
73
|
+
|
|
74
|
+
Todos los componentes están cubiertos por tests unitarios (ver carpeta tests/).
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pytest --cov=fletplus
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
# 🛠️ Contribuir
|
|
81
|
+
|
|
82
|
+
Las contribuciones son bienvenidas:
|
|
83
|
+
|
|
84
|
+
1. **Haz un fork**
|
|
85
|
+
|
|
86
|
+
2. **Crea tu rama**: git checkout -b feature/nueva-funcionalidad
|
|
87
|
+
|
|
88
|
+
3. **Abre un PR** explicando el cambio
|
|
89
|
+
|
|
90
|
+
# 📄 Licencia
|
|
91
|
+
|
|
92
|
+
MIT License
|
|
93
|
+
|
|
94
|
+
Copyright (c) 2025 Adolfo González
|
|
95
|
+
|
|
96
|
+
# 💬 Contacto
|
|
97
|
+
|
|
98
|
+
Desarrollado por Adolfo González Hernández.
|
|
99
|
+
|
|
100
|
+
**email**: adolfogonzal@gmail.com
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
fletplus/__init__.py
|
|
6
|
+
fletplus/core.py
|
|
7
|
+
fletplus.egg-info/PKG-INFO
|
|
8
|
+
fletplus.egg-info/SOURCES.txt
|
|
9
|
+
fletplus.egg-info/dependency_links.txt
|
|
10
|
+
fletplus.egg-info/requires.txt
|
|
11
|
+
fletplus.egg-info/top_level.txt
|
|
12
|
+
fletplus/components/__init__.py
|
|
13
|
+
fletplus/components/responsive_grid.py
|
|
14
|
+
fletplus/components/sidebar_admin.py
|
|
15
|
+
fletplus/components/smart_table.py
|
|
16
|
+
fletplus/themes/__init__.py
|
|
17
|
+
fletplus/themes/theme_manager.py
|
|
18
|
+
fletplus/utils/__init__.py
|
|
19
|
+
fletplus/utils/responsive_manager.py
|
|
20
|
+
tests/test_all.py
|
|
21
|
+
tests/test_fletplus_app.py
|
|
22
|
+
tests/test_responsive_grid.py
|
|
23
|
+
tests/test_sidebar_admin.py
|
|
24
|
+
tests/test_smart_table.py
|
|
25
|
+
tests/test_theme_manager.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flet>=0.27.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fletplus
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fletplus"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Componentes visuales avanzados para Flet en Python"
|
|
9
|
+
authors = [
|
|
10
|
+
{ name="Adolfo González", email="adolfogonzal@gmail.com" }
|
|
11
|
+
]
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
|
|
16
|
+
dependencies = [
|
|
17
|
+
"flet>=0.27.0"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/Alphonsus411/fletplus"
|
|
22
|
+
Repository = "https://github.com/Alphonsus411/fletplus"
|
|
23
|
+
Documentation = "https://github.com/Alphonsus411/fletplus#readme"
|
fletplus-0.1.0/setup.cfg
ADDED
fletplus-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="fletplus",
|
|
5
|
+
version="0.1.0",
|
|
6
|
+
author="Adolfo González Hernández",
|
|
7
|
+
author_email="adolfogonzal@gmail.com",
|
|
8
|
+
description="Componentes avanzados y utilidades para apps Flet en Python",
|
|
9
|
+
long_description=open("README.md", encoding="utf-8").read(),
|
|
10
|
+
long_description_content_type="text/markdown",
|
|
11
|
+
url="https://github.com/tu_usuario/fletplus", # Cambia esto si lo subes a GitHub
|
|
12
|
+
project_urls={
|
|
13
|
+
"Bug Tracker": "https://github.com/Alphonsus411/fletplus/issues",
|
|
14
|
+
},
|
|
15
|
+
license="MIT",
|
|
16
|
+
packages=find_packages(),
|
|
17
|
+
include_package_data=True,
|
|
18
|
+
install_requires=[
|
|
19
|
+
"flet>=0.8.0", # Ajusta según versión actual de Flet
|
|
20
|
+
],
|
|
21
|
+
classifiers=[
|
|
22
|
+
"Development Status :: 3 - Alpha",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Topic :: Software Development :: User Interfaces",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
],
|
|
29
|
+
python_requires='>=3.8',
|
|
30
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# tests/test_all.py
|
|
2
|
+
|
|
3
|
+
from tests.test_smart_table import test_smart_table_full_behavior
|
|
4
|
+
from tests.test_sidebar_admin import test_sidebar_admin_build_and_selection
|
|
5
|
+
from tests.test_responsive_grid import test_responsive_grid_builds_correctly
|
|
6
|
+
from tests.test_theme_manager import test_theme_manager_initialization_and_toggle
|
|
7
|
+
from tests.test_fletplus_app import test_fletplus_app_initialization_and_routing
|
|
8
|
+
|
|
9
|
+
def test_all_components():
|
|
10
|
+
test_smart_table_full_behavior()
|
|
11
|
+
test_sidebar_admin_build_and_selection()
|
|
12
|
+
test_responsive_grid_builds_correctly()
|
|
13
|
+
test_theme_manager_initialization_and_toggle()
|
|
14
|
+
test_fletplus_app_initialization_and_routing()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
|
|
3
|
+
from fletplus.core import FletPlusApp
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DummyPage:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.title = ""
|
|
9
|
+
self.controls = []
|
|
10
|
+
self.theme = None
|
|
11
|
+
self.theme_mode = None
|
|
12
|
+
self.scroll = None
|
|
13
|
+
self.horizontal_alignment = None
|
|
14
|
+
self.updated = False
|
|
15
|
+
|
|
16
|
+
def add(self, *controls):
|
|
17
|
+
self.controls.extend(controls)
|
|
18
|
+
|
|
19
|
+
def update(self):
|
|
20
|
+
self.updated = True
|
|
21
|
+
|
|
22
|
+
def test_fletplus_app_initialization_and_routing():
|
|
23
|
+
# Definir dos pantallas de prueba
|
|
24
|
+
def home_view():
|
|
25
|
+
return ft.Text("Inicio")
|
|
26
|
+
|
|
27
|
+
def users_view():
|
|
28
|
+
return ft.Text("Usuarios")
|
|
29
|
+
|
|
30
|
+
routes = {
|
|
31
|
+
"Inicio": home_view,
|
|
32
|
+
"Usuarios": users_view
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
sidebar_items = [
|
|
36
|
+
{"title": "Inicio", "icon": ft.icons.HOME},
|
|
37
|
+
{"title": "Usuarios", "icon": ft.icons.PEOPLE}
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# Crear instancia falsa de la página
|
|
41
|
+
page = DummyPage()
|
|
42
|
+
|
|
43
|
+
# Crear la app sin iniciar Flet
|
|
44
|
+
app = FletPlusApp(page, routes, sidebar_items, title="TestApp")
|
|
45
|
+
|
|
46
|
+
# Simular construcción
|
|
47
|
+
app.build()
|
|
48
|
+
|
|
49
|
+
# Verificaciones básicas
|
|
50
|
+
assert page.title == "TestApp"
|
|
51
|
+
assert len(page.controls) == 1 # Un solo ft.Row
|
|
52
|
+
assert app.content_container.content is not None
|
|
53
|
+
assert isinstance(app.content_container.content, ft.Text)
|
|
54
|
+
assert app.content_container.content.value == "Inicio"
|
|
55
|
+
|
|
56
|
+
# Simular navegación a la segunda página
|
|
57
|
+
app._on_nav(1)
|
|
58
|
+
assert app.content_container.content.value == "Usuarios"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
from fletplus.components.responsive_grid import ResponsiveGrid
|
|
3
|
+
|
|
4
|
+
def test_responsive_grid_builds_correctly():
|
|
5
|
+
# Crear una lista de widgets dummy
|
|
6
|
+
items = [
|
|
7
|
+
ft.Text(f"Elemento {i}") for i in range(4)
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
# Breakpoints definidos manualmente
|
|
11
|
+
breakpoints = {
|
|
12
|
+
0: 1,
|
|
13
|
+
600: 2,
|
|
14
|
+
900: 4
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
grid = ResponsiveGrid(children=items, breakpoints=breakpoints, spacing=5)
|
|
18
|
+
|
|
19
|
+
# Simular un ancho de 900px (esperamos 4 columnas)
|
|
20
|
+
layout = grid.build(page_width=900)
|
|
21
|
+
|
|
22
|
+
# Validaciones
|
|
23
|
+
assert isinstance(layout, ft.ResponsiveRow)
|
|
24
|
+
assert len(layout.controls) == len(items)
|
|
25
|
+
|
|
26
|
+
# Cada contenedor debe tener col=3 (12/4 columnas)
|
|
27
|
+
for container in layout.controls:
|
|
28
|
+
assert isinstance(container, ft.Container)
|
|
29
|
+
assert container.col == 3
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
from fletplus.components.sidebar_admin import SidebarAdmin
|
|
3
|
+
|
|
4
|
+
def test_sidebar_admin_build_and_selection():
|
|
5
|
+
selected = []
|
|
6
|
+
|
|
7
|
+
def on_select(index):
|
|
8
|
+
selected.append(index)
|
|
9
|
+
|
|
10
|
+
menu_items = [
|
|
11
|
+
{"title": "Inicio", "icon": ft.icons.HOME},
|
|
12
|
+
{"title": "Usuarios", "icon": ft.icons.PEOPLE},
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
sidebar = SidebarAdmin(menu_items=menu_items, on_select=on_select)
|
|
16
|
+
control = sidebar.build()
|
|
17
|
+
|
|
18
|
+
# Comprobar que se construye un Container con Column interna
|
|
19
|
+
assert isinstance(control, ft.Container)
|
|
20
|
+
assert isinstance(control.content, ft.Column)
|
|
21
|
+
assert len(sidebar.tiles) == 2 # Se crearon dos ListTile
|
|
22
|
+
|
|
23
|
+
# Simular evento con un objeto dummy que tiene e.control.page.update()
|
|
24
|
+
class DummyPage:
|
|
25
|
+
def update(self): pass
|
|
26
|
+
|
|
27
|
+
class DummyControl:
|
|
28
|
+
page = DummyPage()
|
|
29
|
+
|
|
30
|
+
class DummyEvent:
|
|
31
|
+
control = DummyControl()
|
|
32
|
+
|
|
33
|
+
# Ejecutar selección
|
|
34
|
+
sidebar._select_item(1, DummyEvent())
|
|
35
|
+
|
|
36
|
+
assert sidebar.selected_index == 1
|
|
37
|
+
assert selected == [1]
|
|
38
|
+
assert sidebar.tiles[1].selected is True
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
from fletplus.components.smart_table import SmartTable
|
|
3
|
+
|
|
4
|
+
class DummyPage:
|
|
5
|
+
def update(self): pass
|
|
6
|
+
|
|
7
|
+
class DummyControl:
|
|
8
|
+
page = DummyPage()
|
|
9
|
+
|
|
10
|
+
class DummyEvent:
|
|
11
|
+
def __init__(self, column_index):
|
|
12
|
+
self.column_index = column_index
|
|
13
|
+
self.control = DummyControl()
|
|
14
|
+
|
|
15
|
+
def test_smart_table_full_behavior():
|
|
16
|
+
columns = ["ID", "Nombre"]
|
|
17
|
+
rows = [
|
|
18
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("2")), ft.DataCell(ft.Text("Bob"))]),
|
|
19
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("1")), ft.DataCell(ft.Text("Alice"))]),
|
|
20
|
+
ft.DataRow(cells=[ft.DataCell(ft.Text("3")), ft.DataCell(ft.Text("Charlie"))]),
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
table = SmartTable(columns, rows, page_size=2)
|
|
24
|
+
|
|
25
|
+
assert table.current_page == 0
|
|
26
|
+
assert table.page_size == 2
|
|
27
|
+
assert table.sort_ascending is True
|
|
28
|
+
assert table.sorted_column is None
|
|
29
|
+
|
|
30
|
+
built = table.build()
|
|
31
|
+
assert isinstance(built, ft.Column)
|
|
32
|
+
assert any(isinstance(c, ft.DataTable) for c in built.controls)
|
|
33
|
+
|
|
34
|
+
sort_handler = table._on_sort(0)
|
|
35
|
+
sort_handler(DummyEvent(0))
|
|
36
|
+
assert table.sorted_column == 0
|
|
37
|
+
assert table.sort_ascending is True
|
|
38
|
+
assert table.rows[0].cells[0].content.value == "1"
|
|
39
|
+
|
|
40
|
+
sort_handler(DummyEvent(0))
|
|
41
|
+
assert table.sort_ascending is False
|
|
42
|
+
assert table.rows[0].cells[0].content.value == "3"
|
|
43
|
+
|
|
44
|
+
table._next_page(DummyEvent(0))
|
|
45
|
+
assert table.current_page == 1
|
|
46
|
+
|
|
47
|
+
table._previous_page(DummyEvent(0))
|
|
48
|
+
assert table.current_page == 0
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
from fletplus.themes.theme_manager import ThemeManager
|
|
3
|
+
|
|
4
|
+
class DummyPage:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.theme = None
|
|
7
|
+
self.theme_mode = None
|
|
8
|
+
self.updated = False
|
|
9
|
+
|
|
10
|
+
def update(self):
|
|
11
|
+
self.updated = True
|
|
12
|
+
|
|
13
|
+
def test_theme_manager_initialization_and_toggle():
|
|
14
|
+
page = DummyPage()
|
|
15
|
+
theme = ThemeManager(
|
|
16
|
+
page=page,
|
|
17
|
+
primary_color=ft.colors.RED
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
theme.apply_theme()
|
|
21
|
+
assert page.theme.color_scheme_seed == ft.colors.RED
|
|
22
|
+
assert page.theme_mode == ft.ThemeMode.LIGHT
|
|
23
|
+
assert page.updated
|
|
24
|
+
|
|
25
|
+
page.updated = False
|
|
26
|
+
theme.toggle_dark_mode()
|
|
27
|
+
assert page.theme_mode == ft.ThemeMode.DARK
|
|
28
|
+
assert page.updated
|
|
29
|
+
|
|
30
|
+
page.updated = False
|
|
31
|
+
theme.set_primary_color(ft.colors.GREEN)
|
|
32
|
+
assert page.theme.color_scheme_seed == ft.colors.GREEN
|
|
33
|
+
assert page.updated
|