SaaS-FrameWork 0.1.1__py3-none-any.whl

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.
Files changed (82) hide show
  1. components/__init__.py +119 -0
  2. components/buttons/DEx_BtnFAB.py +37 -0
  3. components/buttons/DEx_BtnFilled.py +20 -0
  4. components/buttons/DEx_BtnGhost.py +20 -0
  5. components/buttons/DEx_BtnIcon.py +18 -0
  6. components/buttons/DEx_BtnPrimary.py +21 -0
  7. components/buttons/DEx_BtnSegmented.py +45 -0
  8. components/buttons/DEx_BtnText.py +19 -0
  9. components/buttons/DEx_BtnTonal.py +20 -0
  10. components/buttons/DEx_MenuBar.py +61 -0
  11. components/buttons/DEx_PopupMenu.py +43 -0
  12. components/buttons/__init__.py +0 -0
  13. components/cards/DEx_Card.py +23 -0
  14. components/cards/DEx_ContactCard.py +52 -0
  15. components/cards/DEx_MetricCard.py +36 -0
  16. components/cards/__init__.py +0 -0
  17. components/data/DEx_DataTable.py +82 -0
  18. components/data/DEx_ExpansionPanel.py +39 -0
  19. components/data/DEx_ExpansionTile.py +29 -0
  20. components/data/DEx_GridView.py +65 -0
  21. components/data/DEx_ListTile.py +27 -0
  22. components/data/DEx_ListView.py +65 -0
  23. components/data/DEx_PipelineColumn.py +74 -0
  24. components/data/DEx_TreeView.py +536 -0
  25. components/data/__init__.py +0 -0
  26. components/feedback/DEx_AlertDialog.py +26 -0
  27. components/feedback/DEx_Banner.py +19 -0
  28. components/feedback/DEx_BottomSheet.py +37 -0
  29. components/feedback/DEx_Notify.py +15 -0
  30. components/feedback/DEx_ProgressBar.py +34 -0
  31. components/feedback/DEx_ProgressRing.py +52 -0
  32. components/feedback/DEx_Shimmer.py +84 -0
  33. components/feedback/DEx_Tooltip.py +24 -0
  34. components/feedback/__init__.py +0 -0
  35. components/inputs/DEx_AutoComplete.py +29 -0
  36. components/inputs/DEx_Checkbox.py +27 -0
  37. components/inputs/DEx_Chip.py +33 -0
  38. components/inputs/DEx_DatePicker.py +72 -0
  39. components/inputs/DEx_Dropdown.py +25 -0
  40. components/inputs/DEx_FilePicker.py +70 -0
  41. components/inputs/DEx_Radio.py +37 -0
  42. components/inputs/DEx_SearchBar.py +37 -0
  43. components/inputs/DEx_SearchField.py +9 -0
  44. components/inputs/DEx_Slider.py +49 -0
  45. components/inputs/DEx_Switch.py +22 -0
  46. components/inputs/DEx_TextField.py +29 -0
  47. components/inputs/DEx_TimePicker.py +71 -0
  48. components/inputs/__init__.py +0 -0
  49. components/layout/DEx_AnimatedSwitcher.py +43 -0
  50. components/layout/DEx_AppBar.py +48 -0
  51. components/layout/DEx_AppShell.py +80 -0
  52. components/layout/DEx_Draggable.py +67 -0
  53. components/layout/DEx_GestureDetector.py +52 -0
  54. components/layout/DEx_Header.py +57 -0
  55. components/layout/DEx_NavItem.py +61 -0
  56. components/layout/DEx_NavigationBar.py +30 -0
  57. components/layout/DEx_NavigationDrawer.py +60 -0
  58. components/layout/DEx_NavigationRail.py +42 -0
  59. components/layout/DEx_ResponsiveRow.py +42 -0
  60. components/layout/DEx_Sidebar.py +131 -0
  61. components/layout/DEx_Stack.py +73 -0
  62. components/layout/DEx_Tabs.py +110 -0
  63. components/layout/__init__.py +0 -0
  64. components/metadata.py +98 -0
  65. components/primitives/DEx_Avatar.py +87 -0
  66. components/primitives/DEx_Badge.py +18 -0
  67. components/primitives/DEx_Divider.py +13 -0
  68. components/primitives/DEx_Icon.py +46 -0
  69. components/primitives/DEx_Image.py +55 -0
  70. components/primitives/DEx_Markdown.py +21 -0
  71. components/primitives/DEx_SectionEyebrow.py +22 -0
  72. components/primitives/DEx_StatusDot.py +8 -0
  73. components/primitives/__init__.py +0 -0
  74. components/project.py +23 -0
  75. components/theme/DEx_ColorThemes.py +375 -0
  76. components/theme/DEx_Theme.py +136 -0
  77. components/theme/__init__.py +4 -0
  78. components/versioning.py +103 -0
  79. saas_framework-0.1.1.dist-info/METADATA +450 -0
  80. saas_framework-0.1.1.dist-info/RECORD +82 -0
  81. saas_framework-0.1.1.dist-info/WHEEL +5 -0
  82. saas_framework-0.1.1.dist-info/top_level.txt +1 -0
components/__init__.py ADDED
@@ -0,0 +1,119 @@
1
+ """FrameWork — public API root."""
2
+
3
+ from components.theme.DEx_Theme import Colors, Spacing, build_theme, build_theme_from_color_theme
4
+ from components.theme.DEx_ColorThemes import COLOR_THEMES, DEx_ColorTheme
5
+
6
+ from components.primitives.DEx_Badge import DEx_Badge
7
+ from components.primitives.DEx_Divider import DEx_Divider
8
+ from components.primitives.DEx_SectionEyebrow import DEx_SectionEyebrow
9
+ from components.primitives.DEx_StatusDot import DEx_StatusDot
10
+ from components.primitives.DEx_Avatar import DEx_Avatar, DEx_AvatarGroup
11
+ from components.primitives.DEx_Image import DEx_Image, DEx_ImagePlaceholder
12
+ from components.primitives.DEx_Icon import DEx_Icon, DEx_IconBadge
13
+ from components.primitives.DEx_Markdown import DEx_Markdown
14
+
15
+ from components.cards.DEx_Card import DEx_Card
16
+ from components.cards.DEx_MetricCard import DEx_MetricCard
17
+ from components.cards.DEx_ContactCard import DEx_ContactCard
18
+
19
+ from components.buttons.DEx_BtnPrimary import DEx_BtnPrimary
20
+ from components.buttons.DEx_BtnGhost import DEx_BtnGhost
21
+ from components.buttons.DEx_BtnIcon import DEx_BtnIcon
22
+ from components.buttons.DEx_BtnFilled import DEx_BtnFilled
23
+ from components.buttons.DEx_BtnTonal import DEx_BtnTonal
24
+ from components.buttons.DEx_BtnText import DEx_BtnText
25
+ from components.buttons.DEx_BtnFAB import DEx_BtnFAB
26
+ from components.buttons.DEx_BtnSegmented import DEx_BtnSegmented
27
+ from components.buttons.DEx_MenuBar import DEx_MenuBar
28
+ from components.buttons.DEx_PopupMenu import DEx_PopupMenu
29
+
30
+ from components.inputs.DEx_TextField import DEx_TextField
31
+ from components.inputs.DEx_SearchField import DEx_SearchField
32
+ from components.inputs.DEx_Dropdown import DEx_Dropdown
33
+ from components.inputs.DEx_Checkbox import DEx_Checkbox
34
+ from components.inputs.DEx_Switch import DEx_Switch
35
+ from components.inputs.DEx_Radio import DEx_Radio
36
+ from components.inputs.DEx_Slider import DEx_Slider
37
+ from components.inputs.DEx_Chip import DEx_Chip
38
+ from components.inputs.DEx_AutoComplete import DEx_AutoComplete
39
+ from components.inputs.DEx_SearchBar import DEx_SearchBar
40
+ from components.inputs.DEx_DatePicker import DEx_DateField, DEx_DateRangeField
41
+ from components.inputs.DEx_TimePicker import DEx_TimeField, DEx_TimeRangeField
42
+ from components.inputs.DEx_FilePicker import DEx_FilePicker, DEx_FilePickerButton
43
+
44
+ from components.data.DEx_DataTable import DEx_DataTable
45
+ from components.data.DEx_PipelineColumn import DEx_PipelineColumn
46
+ from components.data.DEx_TreeView import DEx_TreeView
47
+ from components.data.DEx_ListTile import DEx_ListTile
48
+ from components.data.DEx_ExpansionTile import DEx_ExpansionTile
49
+ from components.data.DEx_ExpansionPanel import DEx_ExpansionPanel
50
+ from components.data.DEx_ListView import DEx_ListView
51
+ from components.data.DEx_GridView import DEx_GridView
52
+
53
+ from components.layout.DEx_AppShell import DEx_AppShell
54
+ from components.layout.DEx_Sidebar import DEx_Sidebar
55
+ from components.layout.DEx_Header import DEx_Header
56
+ from components.layout.DEx_NavItem import DEx_NavItem
57
+ from components.layout.DEx_Tabs import DEx_Tabs, DEx_Tab
58
+ from components.layout.DEx_NavigationBar import DEx_NavigationBar
59
+ from components.layout.DEx_NavigationRail import DEx_NavigationRail
60
+ from components.layout.DEx_NavigationDrawer import DEx_NavigationDrawer
61
+ from components.layout.DEx_AppBar import DEx_AppBar, DEx_BottomAppBar
62
+ from components.layout.DEx_Stack import DEx_Stack, DEx_StackOverlay, DEx_StackBadge
63
+ from components.layout.DEx_ResponsiveRow import DEx_ResponsiveRow, DEx_Col
64
+ from components.layout.DEx_GestureDetector import DEx_GestureDetector, DEx_Hoverable
65
+ from components.layout.DEx_Draggable import DEx_Draggable, DEx_DragTarget
66
+ from components.layout.DEx_AnimatedSwitcher import DEx_AnimatedSwitcher, DEx_AnimatedContainer
67
+
68
+ from components.feedback.DEx_Notify import DEx_Notify
69
+ from components.feedback.DEx_AlertDialog import DEx_AlertDialog
70
+ from components.feedback.DEx_BottomSheet import DEx_BottomSheet
71
+ from components.feedback.DEx_Banner import DEx_Banner
72
+ from components.feedback.DEx_Tooltip import DEx_Tooltip
73
+ from components.feedback.DEx_ProgressBar import DEx_ProgressBar
74
+ from components.feedback.DEx_ProgressRing import DEx_ProgressRing
75
+ from components.feedback.DEx_Shimmer import DEx_ShimmerCard, DEx_ShimmerListItem, DEx_ShimmerList
76
+
77
+ __version__ = "0.1.1"
78
+
79
+ __all__ = [
80
+ # theme
81
+ "Colors", "Spacing", "build_theme", "build_theme_from_color_theme", "COLOR_THEMES", "DEx_ColorTheme",
82
+ # primitives
83
+ "DEx_Badge", "DEx_Divider", "DEx_SectionEyebrow", "DEx_StatusDot",
84
+ "DEx_Avatar", "DEx_AvatarGroup", "DEx_Image", "DEx_ImagePlaceholder",
85
+ "DEx_Icon", "DEx_IconBadge", "DEx_Markdown",
86
+ # cards
87
+ "DEx_Card", "DEx_MetricCard", "DEx_ContactCard",
88
+ # buttons
89
+ "DEx_BtnPrimary", "DEx_BtnGhost", "DEx_BtnIcon", "DEx_BtnFilled",
90
+ "DEx_BtnTonal", "DEx_BtnText", "DEx_BtnFAB", "DEx_BtnSegmented",
91
+ "DEx_MenuBar", "DEx_PopupMenu",
92
+ # inputs
93
+ "DEx_TextField", "DEx_SearchField", "DEx_Dropdown", "DEx_Checkbox",
94
+ "DEx_Switch", "DEx_Radio", "DEx_Slider", "DEx_Chip",
95
+ "DEx_AutoComplete", "DEx_SearchBar",
96
+ "DEx_DateField", "DEx_DateRangeField",
97
+ "DEx_TimeField", "DEx_TimeRangeField",
98
+ "DEx_FilePicker", "DEx_FilePickerButton",
99
+ # data
100
+ "DEx_DataTable", "DEx_PipelineColumn", "DEx_TreeView",
101
+ "DEx_ListTile", "DEx_ExpansionTile", "DEx_ExpansionPanel",
102
+ "DEx_ListView", "DEx_GridView",
103
+ # layout
104
+ "DEx_AppShell", "DEx_Sidebar", "DEx_Header", "DEx_NavItem",
105
+ "DEx_Tabs", "DEx_Tab",
106
+ "DEx_NavigationBar", "DEx_NavigationRail", "DEx_NavigationDrawer", "DEx_AppBar",
107
+ "DEx_BottomAppBar",
108
+ "DEx_Stack", "DEx_StackOverlay", "DEx_StackBadge",
109
+ "DEx_ResponsiveRow", "DEx_Col",
110
+ "DEx_GestureDetector", "DEx_Hoverable",
111
+ "DEx_Draggable", "DEx_DragTarget",
112
+ "DEx_AnimatedSwitcher", "DEx_AnimatedContainer",
113
+ # feedback
114
+ "DEx_Notify", "DEx_AlertDialog", "DEx_BottomSheet", "DEx_Banner",
115
+ "DEx_Tooltip", "DEx_ProgressBar", "DEx_ProgressRing",
116
+ "DEx_ShimmerCard", "DEx_ShimmerListItem", "DEx_ShimmerList",
117
+ # meta
118
+ "__version__",
119
+ ]
@@ -0,0 +1,37 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_BtnFAB(
8
+ icon: str,
9
+ label: str = "",
10
+ on_click=None,
11
+ mini: bool = False,
12
+ ) -> ft.FloatingActionButton:
13
+ """Floating action button for the primary creation or positive action on a screen.
14
+
15
+ Pass label for extended FAB; omit for compact circular form.
16
+ """
17
+ if label:
18
+ return ft.FloatingActionButton(
19
+ content=ft.Row(
20
+ [ft.Icon(icon, size=18, color=Colors.TEXT_PRIMARY), ft.Text(label, size=14, color=Colors.TEXT_PRIMARY)],
21
+ spacing=8,
22
+ tight=True,
23
+ ),
24
+ on_click=on_click,
25
+ bgcolor=Colors.ACCENT,
26
+ hover_color=Colors.ACCENT_DARK,
27
+ shape=ft.RoundedRectangleBorder(radius=Spacing.BUTTON_BORDER_RADIUS),
28
+ )
29
+ return ft.FloatingActionButton(
30
+ icon=icon,
31
+ on_click=on_click,
32
+ mini=mini,
33
+ bgcolor=Colors.ACCENT,
34
+ foreground_color=Colors.TEXT_PRIMARY,
35
+ hover_color=Colors.ACCENT_DARK,
36
+ shape=ft.RoundedRectangleBorder(radius=28),
37
+ )
@@ -0,0 +1,20 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_BtnFilled(label: str, on_click=None, icon=None) -> ft.FilledButton:
8
+ """Primary filled button — highest emphasis action in a view."""
9
+ return ft.FilledButton(
10
+ content=label,
11
+ icon=icon,
12
+ on_click=on_click,
13
+ style=ft.ButtonStyle(
14
+ bgcolor=Colors.ACCENT,
15
+ color=Colors.TEXT_PRIMARY,
16
+ overlay_color=Colors.ACCENT_DARK,
17
+ shape=ft.RoundedRectangleBorder(radius=Spacing.BUTTON_BORDER_RADIUS),
18
+ padding=ft.Padding.symmetric(horizontal=Spacing.MD, vertical=Spacing.SM + 2),
19
+ ),
20
+ )
@@ -0,0 +1,20 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_BtnGhost(label: str, on_click=None, icon=None) -> ft.OutlinedButton:
8
+ """Create the versioned secondary outlined button for lower-emphasis actions."""
9
+ return ft.OutlinedButton(
10
+ content=label,
11
+ icon=icon,
12
+ on_click=on_click,
13
+ style=ft.ButtonStyle(
14
+ color=Colors.TEXT_PRIMARY,
15
+ side=ft.BorderSide(1, Colors.BORDER),
16
+ overlay_color=Colors.BG_ELEVATED,
17
+ shape=ft.RoundedRectangleBorder(radius=Spacing.BUTTON_BORDER_RADIUS),
18
+ padding=ft.Padding.symmetric(horizontal=Spacing.MD, vertical=Spacing.SM + 2),
19
+ ),
20
+ )
@@ -0,0 +1,18 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_BtnIcon(icon, on_click=None, tooltip: str = "") -> ft.IconButton:
8
+ """Create the versioned icon-only action button with optional tooltip."""
9
+ return ft.IconButton(
10
+ icon=icon,
11
+ icon_color=Colors.TEXT_SECONDARY,
12
+ tooltip=tooltip,
13
+ on_click=on_click,
14
+ style=ft.ButtonStyle(
15
+ overlay_color=Colors.BG_ELEVATED,
16
+ shape=ft.RoundedRectangleBorder(radius=6),
17
+ ),
18
+ )
@@ -0,0 +1,21 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.1.0"
5
+
6
+
7
+ def DEx_BtnPrimary(label: str, on_click=None, icon=None) -> ft.Button:
8
+ """Create the versioned primary action button for main user commands."""
9
+ return ft.Button(
10
+ content=label,
11
+ icon=icon,
12
+ on_click=on_click,
13
+ bgcolor=Colors.ACCENT,
14
+ color=Colors.TEXT_PRIMARY,
15
+ elevation=1,
16
+ style=ft.ButtonStyle(
17
+ overlay_color=Colors.ACCENT_DARK,
18
+ shape=ft.RoundedRectangleBorder(radius=Spacing.BUTTON_BORDER_RADIUS),
19
+ padding=ft.Padding.symmetric(horizontal=Spacing.MD, vertical=Spacing.SM + 2),
20
+ ),
21
+ )
@@ -0,0 +1,45 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_BtnSegmented(
8
+ segments: list[dict],
9
+ selected: list[str] | set[str] | None = None,
10
+ on_change=None,
11
+ allow_multiple: bool = False,
12
+ allow_empty: bool = False,
13
+ ) -> ft.SegmentedButton:
14
+ """Multi-option toggle control for mutually exclusive or multi-select choices.
15
+
16
+ Each entry in segments: {"value": str, "label": str, "icon": str | None}
17
+ """
18
+ _selected = list(selected) if selected else [segments[0]["value"]]
19
+ return ft.SegmentedButton(
20
+ segments=[
21
+ ft.Segment(
22
+ value=s["value"],
23
+ label=ft.Text(s["label"], size=13),
24
+ icon=ft.Icon(s["icon"]) if s.get("icon") else None,
25
+ )
26
+ for s in segments
27
+ ],
28
+ selected=_selected,
29
+ on_change=on_change,
30
+ allow_multiple_selection=allow_multiple,
31
+ allow_empty_selection=allow_empty,
32
+ style=ft.ButtonStyle(
33
+ bgcolor={
34
+ ft.ControlState.SELECTED: Colors.ACCENT,
35
+ ft.ControlState.DEFAULT: Colors.BG_ELEVATED,
36
+ },
37
+ color={
38
+ ft.ControlState.SELECTED: Colors.TEXT_PRIMARY,
39
+ ft.ControlState.DEFAULT: Colors.TEXT_SECONDARY,
40
+ },
41
+ side=ft.BorderSide(1, Colors.BORDER),
42
+ shape=ft.RoundedRectangleBorder(radius=Spacing.BUTTON_BORDER_RADIUS),
43
+ padding=ft.Padding.symmetric(horizontal=Spacing.MD, vertical=Spacing.SM),
44
+ ),
45
+ )
@@ -0,0 +1,19 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_BtnText(label: str, on_click=None, icon=None) -> ft.TextButton:
8
+ """Lowest-emphasis action button — no border, no background fill."""
9
+ return ft.TextButton(
10
+ content=label,
11
+ icon=icon,
12
+ on_click=on_click,
13
+ style=ft.ButtonStyle(
14
+ color=Colors.ACCENT,
15
+ overlay_color=Colors.BG_ELEVATED,
16
+ shape=ft.RoundedRectangleBorder(radius=Spacing.BUTTON_BORDER_RADIUS),
17
+ padding=ft.Padding.symmetric(horizontal=Spacing.SM, vertical=Spacing.SM + 2),
18
+ ),
19
+ )
@@ -0,0 +1,20 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_BtnTonal(label: str, on_click=None, icon=None) -> ft.FilledTonalButton:
8
+ """Secondary filled button with muted emphasis — between outlined and filled."""
9
+ return ft.FilledTonalButton(
10
+ content=label,
11
+ icon=icon,
12
+ on_click=on_click,
13
+ style=ft.ButtonStyle(
14
+ bgcolor=Colors.BG_ELEVATED,
15
+ color=Colors.ACCENT,
16
+ overlay_color=Colors.ACCENT_DARK,
17
+ shape=ft.RoundedRectangleBorder(radius=Spacing.BUTTON_BORDER_RADIUS),
18
+ padding=ft.Padding.symmetric(horizontal=Spacing.MD, vertical=Spacing.SM + 2),
19
+ ),
20
+ )
@@ -0,0 +1,61 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+ _ITEM_STYLE = ft.ButtonStyle(
7
+ bgcolor={
8
+ ft.ControlState.HOVERED: Colors.BG_ELEVATED,
9
+ ft.ControlState.DEFAULT: Colors.BG_SURFACE,
10
+ },
11
+ color=Colors.TEXT_PRIMARY,
12
+ overlay_color=Colors.BG_ELEVATED,
13
+ padding=ft.Padding.symmetric(horizontal=Spacing.MD, vertical=Spacing.SM),
14
+ )
15
+
16
+
17
+ def DEx_MenuBarItem(
18
+ label: str,
19
+ children: list[ft.MenuItemButton | ft.SubmenuButton],
20
+ ) -> ft.SubmenuButton:
21
+ """Top-level menu entry inside a DEx_MenuBar."""
22
+ return ft.SubmenuButton(
23
+ content=ft.Text(label, size=13, color=Colors.TEXT_PRIMARY),
24
+ controls=children,
25
+ style=_ITEM_STYLE,
26
+ )
27
+
28
+
29
+ def DEx_MenuItem(
30
+ label: str,
31
+ icon: str | None = None,
32
+ on_click=None,
33
+ shortcut: str = "",
34
+ ) -> ft.MenuItemButton:
35
+ """Single clickable item inside a submenu."""
36
+ return ft.MenuItemButton(
37
+ content=ft.Row(
38
+ [
39
+ ft.Text(label, size=13, color=Colors.TEXT_PRIMARY, expand=True),
40
+ ft.Text(shortcut, size=11, color=Colors.TEXT_MUTED) if shortcut else ft.Container(),
41
+ ],
42
+ spacing=Spacing.MD,
43
+ ),
44
+ leading=ft.Icon(icon, size=16, color=Colors.TEXT_SECONDARY) if icon else None,
45
+ on_click=on_click,
46
+ style=_ITEM_STYLE,
47
+ )
48
+
49
+
50
+ def DEx_MenuBar(items: list[ft.SubmenuButton]) -> ft.MenuBar:
51
+ """Horizontal application menu bar styled with DEx tokens."""
52
+ return ft.MenuBar(
53
+ controls=items,
54
+ style=ft.MenuStyle(
55
+ bgcolor=Colors.BG_SURFACE,
56
+ shadow_color=ft.Colors.TRANSPARENT,
57
+ side=ft.BorderSide(0, ft.Colors.TRANSPARENT),
58
+ shape=ft.RoundedRectangleBorder(radius=0),
59
+ padding=ft.Padding.symmetric(horizontal=Spacing.SM, vertical=0),
60
+ ),
61
+ )
@@ -0,0 +1,43 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_PopupMenuItem(
8
+ label: str,
9
+ icon: str | None = None,
10
+ on_click=None,
11
+ checked: bool | None = None,
12
+ ) -> ft.PopupMenuItem:
13
+ """Single item inside a DEx_PopupMenu."""
14
+ row_items: list[ft.Control] = []
15
+ if icon:
16
+ row_items.append(ft.Icon(icon, size=16, color=Colors.TEXT_SECONDARY))
17
+ row_items.append(ft.Text(label, size=13, color=Colors.TEXT_PRIMARY))
18
+ return ft.PopupMenuItem(
19
+ content=ft.Row(row_items, spacing=Spacing.SM, tight=True),
20
+ on_click=on_click,
21
+ checked=checked,
22
+ )
23
+
24
+
25
+ def DEx_PopupMenuDivider() -> ft.PopupMenuItem:
26
+ """Visual separator between groups of popup menu items."""
27
+ return ft.PopupMenuItem()
28
+
29
+
30
+ def DEx_PopupMenu(
31
+ items: list[ft.PopupMenuItem],
32
+ icon: str = ft.Icons.MORE_VERT,
33
+ tooltip: str = "Mais opções",
34
+ icon_color: str | None = None,
35
+ ) -> ft.PopupMenuButton:
36
+ """Context/overflow menu triggered by an icon button."""
37
+ return ft.PopupMenuButton(
38
+ icon=icon,
39
+ icon_color=icon_color or Colors.TEXT_SECONDARY,
40
+ tooltip=tooltip,
41
+ items=items,
42
+ menu_position=ft.PopupMenuPosition.UNDER,
43
+ )
File without changes
@@ -0,0 +1,23 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_Card(
8
+ content: ft.Control,
9
+ padding: int = Spacing.MD,
10
+ elevated: bool = False,
11
+ width: int = 300,
12
+ expand: bool = False,
13
+ ) -> ft.Container:
14
+ """Create the versioned base card surface used by higher-level components."""
15
+ return ft.Container(
16
+ content=content,
17
+ bgcolor=Colors.BG_ELEVATED if elevated else Colors.BG_SURFACE,
18
+ border=ft.Border.all(1, Colors.BORDER),
19
+ border_radius=Spacing.CARD_BORDER_RADIUS,
20
+ padding=padding,
21
+ width=None if expand else width,
22
+ expand=expand,
23
+ )
@@ -0,0 +1,52 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+ from components.cards.DEx_Card import DEx_Card
4
+ from components.primitives.DEx_Badge import DEx_Badge
5
+ from components.primitives.DEx_Divider import DEx_Divider
6
+
7
+ COMPONENT_VERSION = "0.0.1"
8
+
9
+
10
+ def DEx_ContactCard(
11
+ name: str,
12
+ role: str,
13
+ company: str,
14
+ email: str = None,
15
+ tag: str = None,
16
+ value: str = None,
17
+ ) -> ft.Container:
18
+ """Create the versioned contact/entity card with avatar and optional footer."""
19
+ initials = "".join(p[0].upper() for p in name.split()[:2])
20
+
21
+ avatar = ft.Container(
22
+ content=ft.Text(initials, size=14, weight=ft.FontWeight.W_700, color=Colors.ACCENT),
23
+ width=42, height=42,
24
+ bgcolor=f"{Colors.ACCENT}22",
25
+ border_radius=21,
26
+ alignment=ft.Alignment(0, 0),
27
+ )
28
+
29
+ info = ft.Column([
30
+ ft.Text(name, size=14, weight=ft.FontWeight.W_600, color=Colors.TEXT_PRIMARY),
31
+ ft.Text(f"{role} · {company}", size=12, color=Colors.TEXT_SECONDARY),
32
+ ft.Text(email, size=12, color=Colors.DATA) if email else ft.Container(),
33
+ ], spacing=2, tight=True)
34
+
35
+ footer_controls = []
36
+ if tag:
37
+ footer_controls.append(
38
+ DEx_Badge(tag, color=f"{Colors.ACCENT}33", text_color=Colors.ACCENT)
39
+ )
40
+ if value:
41
+ footer_controls.append(
42
+ ft.Text(value, size=13, weight=ft.FontWeight.W_700, color=Colors.TEXT_PRIMARY)
43
+ )
44
+
45
+ return DEx_Card(ft.Column([
46
+ ft.Row([avatar, info], spacing=Spacing.MD),
47
+ DEx_Divider() if footer_controls else ft.Container(),
48
+ ft.Row(
49
+ footer_controls,
50
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
51
+ ) if footer_controls else ft.Container(),
52
+ ], spacing=Spacing.SM, tight=True))
@@ -0,0 +1,36 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+ from components.cards.DEx_Card import DEx_Card
4
+ from components.primitives.DEx_StatusDot import DEx_StatusDot
5
+
6
+ COMPONENT_VERSION = "0.0.1"
7
+
8
+
9
+ def DEx_MetricCard(
10
+ label: str,
11
+ value: str,
12
+ delta: str = None,
13
+ delta_positive: bool = True,
14
+ icon=None,
15
+ ) -> ft.Container:
16
+ """Create the versioned metric card for KPI values and period deltas."""
17
+ delta_color = Colors.SUCCESS if delta_positive else Colors.DANGER
18
+
19
+ header_row = [ft.Text(label, size=12, color=Colors.TEXT_SECONDARY, weight=ft.FontWeight.W_500)]
20
+ if icon:
21
+ header_row.insert(0, ft.Icon(icon, size=14, color=Colors.TEXT_MUTED))
22
+
23
+ controls = [
24
+ ft.Row(header_row, spacing=Spacing.XS),
25
+ ft.Text(value, size=26, weight=ft.FontWeight.W_700, color=Colors.TEXT_PRIMARY),
26
+ ]
27
+ if delta:
28
+ controls.append(
29
+ ft.Row([
30
+ DEx_StatusDot(delta_color),
31
+ ft.Text(delta, size=12, color=delta_color, weight=ft.FontWeight.W_600),
32
+ ft.Text("vs período anterior", size=12, color=Colors.TEXT_MUTED),
33
+ ], spacing=Spacing.XS)
34
+ )
35
+
36
+ return DEx_Card(ft.Column(controls, spacing=Spacing.SM, tight=True))
File without changes
@@ -0,0 +1,82 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def _state_placeholder(state: str, empty_message: str, error_message: str) -> ft.Control:
8
+ if state == "loading":
9
+ return ft.Container(
10
+ content=ft.Column(
11
+ [ft.ProgressRing(width=32, height=32, stroke_width=3, color=Colors.ACCENT)],
12
+ alignment=ft.MainAxisAlignment.CENTER,
13
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
14
+ ),
15
+ padding=Spacing.XL,
16
+ )
17
+ if state == "empty":
18
+ return ft.Container(
19
+ content=ft.Column(
20
+ [ft.Icon(ft.Icons.INBOX_ROUNDED, size=40, color=Colors.TEXT_MUTED),
21
+ ft.Text(empty_message, size=13, color=Colors.TEXT_MUTED)],
22
+ alignment=ft.MainAxisAlignment.CENTER,
23
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
24
+ spacing=Spacing.SM,
25
+ ),
26
+ padding=Spacing.XL,
27
+ )
28
+ return ft.Container(
29
+ content=ft.Column(
30
+ [ft.Icon(ft.Icons.ERROR_ROUNDED, size=40, color=Colors.DANGER),
31
+ ft.Text(error_message, size=13, color=Colors.DANGER)],
32
+ alignment=ft.MainAxisAlignment.CENTER,
33
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
34
+ spacing=Spacing.SM,
35
+ ),
36
+ padding=Spacing.XL,
37
+ )
38
+
39
+
40
+ def DEx_DataTable(
41
+ columns: list[str],
42
+ rows: list[list[str]],
43
+ on_row_click=None,
44
+ state: str | None = None,
45
+ empty_message: str = "Nenhum registro encontrado.",
46
+ error_message: str = "Erro ao carregar os dados.",
47
+ ) -> ft.Control:
48
+ """Create the versioned data table with DEx headers, borders, and rows."""
49
+ if state in ("loading", "empty", "error"):
50
+ return _state_placeholder(state, empty_message, error_message)
51
+ return ft.DataTable(
52
+ columns=[
53
+ ft.DataColumn(
54
+ ft.Text(
55
+ col.upper(),
56
+ size=11,
57
+ weight=ft.FontWeight.W_600,
58
+ color=Colors.TEXT_MUTED,
59
+ style=ft.TextStyle(letter_spacing=1.2),
60
+ )
61
+ )
62
+ for col in columns
63
+ ],
64
+ rows=[
65
+ ft.DataRow(
66
+ cells=[
67
+ ft.DataCell(ft.Text(cell, size=13, color=Colors.TEXT_PRIMARY))
68
+ for cell in row
69
+ ],
70
+ on_select_change=on_row_click,
71
+ )
72
+ for row in rows
73
+ ],
74
+ border=ft.Border.all(1, Colors.BORDER),
75
+ border_radius=Spacing.CARD_BORDER_RADIUS,
76
+ horizontal_lines=ft.BorderSide(1, Colors.BORDER),
77
+ heading_row_color=Colors.BG_ELEVATED,
78
+ heading_row_height=44,
79
+ data_row_min_height=48,
80
+ data_row_max_height=56,
81
+ column_spacing=Spacing.LG,
82
+ )
@@ -0,0 +1,39 @@
1
+ import flet as ft
2
+ from components.theme.DEx_Theme import Colors, Spacing
3
+
4
+ COMPONENT_VERSION = "0.0.1"
5
+
6
+
7
+ def DEx_ExpansionPanel(
8
+ title: str,
9
+ content: ft.Control,
10
+ subtitle: str = "",
11
+ expanded: bool = False,
12
+ ) -> ft.ExpansionPanel:
13
+ """Single panel for use inside DEx_ExpansionPanelList."""
14
+ header = ft.ListTile(
15
+ title=ft.Text(title, size=14, color=Colors.TEXT_PRIMARY),
16
+ subtitle=ft.Text(subtitle, size=11, color=Colors.TEXT_SECONDARY) if subtitle else None,
17
+ )
18
+ return ft.ExpansionPanel(
19
+ header=header,
20
+ content=ft.Container(
21
+ content=content,
22
+ padding=ft.Padding.symmetric(horizontal=Spacing.MD, vertical=Spacing.SM),
23
+ ),
24
+ expanded=expanded,
25
+ bgcolor=Colors.BG_ELEVATED,
26
+ )
27
+
28
+
29
+ def DEx_ExpansionPanelList(
30
+ panels: list[ft.ExpansionPanel],
31
+ on_change=None,
32
+ ) -> ft.ExpansionPanelList:
33
+ """Accordion-style list of expandable panels."""
34
+ return ft.ExpansionPanelList(
35
+ controls=panels,
36
+ on_change=on_change,
37
+ divider_color=Colors.BORDER,
38
+ elevation=0,
39
+ )