flask-admin-tabler 2026.2.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 (32) hide show
  1. flask_admin_tabler/__init__.py +3 -0
  2. flask_admin_tabler/static/admin/css/tabler/admin.css +74 -0
  3. flask_admin_tabler/static/admin/css/tabler/rediscli.css +48 -0
  4. flask_admin_tabler/templates/tabler/admin/actions.html +38 -0
  5. flask_admin_tabler/templates/tabler/admin/base.html +115 -0
  6. flask_admin_tabler/templates/tabler/admin/file/form.html +9 -0
  7. flask_admin_tabler/templates/tabler/admin/file/list.html +195 -0
  8. flask_admin_tabler/templates/tabler/admin/file/modals/form.html +19 -0
  9. flask_admin_tabler/templates/tabler/admin/index.html +4 -0
  10. flask_admin_tabler/templates/tabler/admin/layout.html +103 -0
  11. flask_admin_tabler/templates/tabler/admin/lib.html +293 -0
  12. flask_admin_tabler/templates/tabler/admin/master.html +1 -0
  13. flask_admin_tabler/templates/tabler/admin/model/create.html +30 -0
  14. flask_admin_tabler/templates/tabler/admin/model/details.html +53 -0
  15. flask_admin_tabler/templates/tabler/admin/model/edit.html +40 -0
  16. flask_admin_tabler/templates/tabler/admin/model/inline_field_list.html +15 -0
  17. flask_admin_tabler/templates/tabler/admin/model/inline_form.html +4 -0
  18. flask_admin_tabler/templates/tabler/admin/model/inline_list_base.html +45 -0
  19. flask_admin_tabler/templates/tabler/admin/model/layout.html +110 -0
  20. flask_admin_tabler/templates/tabler/admin/model/list.html +198 -0
  21. flask_admin_tabler/templates/tabler/admin/model/modals/create.html +26 -0
  22. flask_admin_tabler/templates/tabler/admin/model/modals/details.html +40 -0
  23. flask_admin_tabler/templates/tabler/admin/model/modals/edit.html +27 -0
  24. flask_admin_tabler/templates/tabler/admin/model/row_actions.html +38 -0
  25. flask_admin_tabler/templates/tabler/admin/rediscli/console.html +27 -0
  26. flask_admin_tabler/templates/tabler/admin/rediscli/response.html +32 -0
  27. flask_admin_tabler/templates/tabler/admin/static.html +3 -0
  28. flask_admin_tabler/theme.py +55 -0
  29. flask_admin_tabler-2026.2.1.dist-info/METADATA +111 -0
  30. flask_admin_tabler-2026.2.1.dist-info/RECORD +32 -0
  31. flask_admin_tabler-2026.2.1.dist-info/WHEEL +4 -0
  32. flask_admin_tabler-2026.2.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,3 @@
1
+ from flask_admin_tabler.theme import TablerTheme
2
+
3
+ __all__ = ["TablerTheme"]
@@ -0,0 +1,74 @@
1
+ /* Flask-Admin Tabler theme custom styles */
2
+
3
+ /* List view action buttons */
4
+ td.list-buttons-column {
5
+ white-space: nowrap;
6
+ width: 1%;
7
+ }
8
+
9
+ a.icon,
10
+ form.icon {
11
+ display: inline-block;
12
+ padding: 0 2px;
13
+ }
14
+
15
+ form.icon button {
16
+ background: none;
17
+ border: none;
18
+ cursor: pointer;
19
+ padding: 0;
20
+ color: inherit;
21
+ }
22
+
23
+ form.icon button:hover,
24
+ a.icon:hover {
25
+ color: var(--tblr-primary);
26
+ }
27
+
28
+ /* Inline form fields */
29
+ .inline-field-list .inline-field {
30
+ margin-bottom: 1rem;
31
+ }
32
+
33
+ /* Filter form table */
34
+ table.filters {
35
+ width: 100%;
36
+ }
37
+
38
+ /* Model list checkbox column */
39
+ .list-checkbox-column {
40
+ width: 1%;
41
+ white-space: nowrap;
42
+ }
43
+
44
+ /* Submit row spacing */
45
+ .submit-row {
46
+ display: flex;
47
+ gap: 0.5rem;
48
+ flex-wrap: wrap;
49
+ }
50
+
51
+ /* Admin form fieldset */
52
+ .admin-form fieldset {
53
+ border: none;
54
+ padding: 0;
55
+ margin: 0;
56
+ }
57
+
58
+ /* Select2 adjustments for Tabler */
59
+ .select2-container .select2-selection--single {
60
+ height: calc(1.5em + 0.75rem + 2px);
61
+ }
62
+
63
+ .select2-container--default .select2-selection--single .select2-selection__rendered {
64
+ line-height: calc(1.5em + 0.75rem);
65
+ }
66
+
67
+ .select2-container--default .select2-selection--single .select2-selection__arrow {
68
+ height: calc(1.5em + 0.75rem);
69
+ }
70
+
71
+ /* Searchable table filter */
72
+ tr.hidden-by-filter {
73
+ display: none;
74
+ }
@@ -0,0 +1,48 @@
1
+ .console {
2
+ position: relative;
3
+ width: 100%;
4
+ min-height: 400px;
5
+ }
6
+
7
+ .console-container {
8
+ border-radius: 4px;
9
+ position: absolute;
10
+ border: 1px solid #d4d4d4;
11
+ padding: 2px;
12
+ overflow: scroll;
13
+ top: 2px;
14
+ left: 2px;
15
+ right: 2px;
16
+ bottom: 5em;
17
+ }
18
+
19
+ .console-line {
20
+ position: absolute;
21
+ left: 2px;
22
+ right: 2px;
23
+ bottom: 2px;
24
+ }
25
+
26
+ .console-line input {
27
+ width: 100%;
28
+ box-sizing: border-box;
29
+ -moz-box-sizing: border-box;
30
+ -webkit-box-sizing: border-box;
31
+ height: 2em;
32
+ }
33
+
34
+ .console .cmd {
35
+ background-color: #f5f5f5;
36
+ padding: 2px;
37
+ margin: 1px;
38
+ }
39
+
40
+ .console .response {
41
+ background-color: #f0f0f0;
42
+ padding: 2px;
43
+ margin: 1px;
44
+ }
45
+
46
+ .console .error {
47
+ color: red;
48
+ }
@@ -0,0 +1,38 @@
1
+ {% import 'admin/static.html' as admin_static with context %}
2
+
3
+ {% macro dropdown(actions, btn_class='nav-link dropdown-toggle') -%}
4
+ <a class="{{ btn_class }}" data-bs-toggle="dropdown" href="javascript:void(0)" role="button" aria-haspopup="true"
5
+ aria-expanded="false">{{ _gettext('With selected') }}</a>
6
+ <div class="dropdown-menu">
7
+ {% for p in actions %}
8
+ <a class="dropdown-item" href="javascript:void(0)"
9
+ onclick="return modelActions.execute('{{ p[0] }}');">{{ _gettext(p[1]) }}</a>
10
+ {% endfor %}
11
+ </div>
12
+ {% endmacro %}
13
+
14
+ {% macro form(actions, url) %}
15
+ {% if actions %}
16
+ <form id="action_form" action="{{ url }}" method="POST" class="d-none">
17
+ {% if action_form.csrf_token is defined and action_form.csrf_token %}
18
+ {{ action_form.csrf_token }}
19
+ {% elif csrf_token is defined and csrf_token %}
20
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
21
+ {% endif %}
22
+ {% if return_url is defined and return_url %}
23
+ {{ action_form.url(value=return_url) }}
24
+ {% else %}
25
+ {{ action_form.url() }}
26
+ {% endif %}
27
+ {{ action_form.action() }}
28
+ </form>
29
+ {% endif %}
30
+ {% endmacro %}
31
+
32
+ {% macro script(message, actions, actions_confirmation) %}
33
+ {% if actions %}
34
+ <div id="actions-confirmation-data" class="d-none">{{ actions_confirmation|tojson|safe }}</div>
35
+ <div id="message-data" class="d-none">{{ message|tojson|safe }}</div>
36
+ <script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='admin/js/actions.js', v='1.0.0') }}"></script>
37
+ {% endif %}
38
+ {% endmacro %}
@@ -0,0 +1,115 @@
1
+ {% import 'admin/layout.html' as layout with context -%}
2
+ {% import 'admin/static.html' as admin_static with context %}
3
+ <!DOCTYPE html>
4
+ <html lang="en">
5
+ <head>
6
+ <title>{% block title %}{% if admin_view.category %}{{ admin_view.category }} - {% endif %}{{ admin_view.name }} - {{ admin_view.admin.name }}{% endblock %}</title>
7
+ {% block head_meta %}
8
+ <meta charset="UTF-8">
9
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
11
+ <meta name="description" content="">
12
+ <meta name="author" content="">
13
+ {% endblock %}
14
+ {% block head_css %}
15
+ <link href="https://cdn.jsdelivr.net/npm/@tabler/core@1.4.0/dist/css/tabler.min.css" rel="stylesheet" {{ admin_csp_nonce_attribute }}>
16
+ {% if admin_view.admin.theme.tabler_icons %}
17
+ <link href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3.31.0/dist/tabler-icons.min.css" rel="stylesheet" {{ admin_csp_nonce_attribute }}>
18
+ {% endif %}
19
+ <link href="{{ url_for('flask_admin_tabler.static', filename='admin/css/tabler/admin.css') }}" rel="stylesheet" {{ admin_csp_nonce_attribute }}>
20
+ {% if admin_view.extra_css %}
21
+ {% for css_url in admin_view.extra_css %}
22
+ <link href="{{ css_url }}" rel="stylesheet" {{ admin_csp_nonce_attribute }}>
23
+ {% endfor %}
24
+ {% endif %}
25
+ {% endblock %}
26
+ {% block head %}
27
+ {% endblock %}
28
+ {% block head_tail %}
29
+ {% endblock %}
30
+ </head>
31
+ <body class="antialiased">
32
+ <div class="page">
33
+ {% block page_body %}
34
+ <!-- Sticky top navbar -->
35
+ <header class="navbar navbar-expand-md sticky-top d-print-none">
36
+ <div class="container-xl">
37
+ {% block brand %}
38
+ <a class="navbar-brand navbar-brand-autodark" href="{{ admin_view.admin.url }}">{{ admin_view.admin.name }}</a>
39
+ {% endblock %}
40
+ <!-- Toggle button for smaller screens -->
41
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-menu"
42
+ aria-controls="navbar-menu" aria-expanded="false" aria-label="Toggle navigation">
43
+ <span class="navbar-toggler-icon"></span>
44
+ </button>
45
+ <!-- Collapsible menu -->
46
+ <div class="collapse navbar-collapse" id="navbar-menu">
47
+ {% block main_menu %}
48
+ <ul class="navbar-nav me-auto">
49
+ {{ layout.menu() }}
50
+ </ul>
51
+ {% endblock %}
52
+ {% block menu_links %}
53
+ <ul class="navbar-nav ms-auto">
54
+ {{ layout.menu_links() }}
55
+ </ul>
56
+ {% endblock %}
57
+ {% block access_control %}
58
+ {% endblock %}
59
+ </div>
60
+ </div>
61
+ </header>
62
+
63
+ <!-- Page wrapper -->
64
+ <div class="page-wrapper">
65
+ <!-- Page header -->
66
+ <div class="page-header d-print-none">
67
+ <div class="container-xl">
68
+ <div class="row g-2 align-items-center">
69
+ <div class="col">
70
+ {% block page_title %}
71
+ <h2 class="page-title">
72
+ {{ layout.menu_icon(admin_view.menu) }}
73
+ {{ admin_view.name }}
74
+ </h2>
75
+ {% endblock %}
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- Page body -->
82
+ <div class="page-body">
83
+ <div class="container-xl">
84
+ {% block messages %}
85
+ {{ layout.messages() }}
86
+ {% endblock %}
87
+
88
+ {# store the jinja2 context for form_rules rendering logic #}
89
+ {% set render_ctx = h.resolve_ctx() %}
90
+
91
+ {% block body %}{% endblock %}
92
+ </div>
93
+ </div>
94
+ </div>
95
+ {% endblock %}
96
+ </div>
97
+
98
+ {% block tail_js %}
99
+ <script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='vendor/jquery.min.js', v='3.5.1') }}" type="text/javascript"></script>
100
+ <script {{ admin_csp_nonce_attribute }} src="https://cdn.jsdelivr.net/npm/@tabler/core@1.4.0/dist/js/tabler.min.js" type="text/javascript"></script>
101
+ <script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='vendor/moment.min.js', v='2.9.4') }}" type="text/javascript"></script>
102
+ <script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='vendor/select2/select2.min.js', v='4.2.1') }}"
103
+ type="text/javascript"></script>
104
+ <script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='admin/js/helpers.js', v='1.0.0') }}" type="text/javascript"></script>
105
+ {% if admin_view.extra_js %}
106
+ {% for js_url in admin_view.extra_js %}
107
+ <script {{ admin_csp_nonce_attribute }} src="{{ js_url }}" type="text/javascript"></script>
108
+ {% endfor %}
109
+ {% endif %}
110
+ {% endblock %}
111
+
112
+ {% block tail %}
113
+ {% endblock %}
114
+ </body>
115
+ </html>
@@ -0,0 +1,9 @@
1
+ {% extends 'admin/master.html' %}
2
+ {% import 'admin/lib.html' as lib with context %}
3
+
4
+ {% block body %}
5
+ {% block header %}<h3>{{ header_text }}</h3>{% endblock %}
6
+ {% block fa_form %}
7
+ {{ lib.render_form(form, dir_url) }}
8
+ {% endblock %}
9
+ {% endblock %}
@@ -0,0 +1,195 @@
1
+ {% extends 'admin/master.html' %}
2
+ {% import 'admin/lib.html' as lib with context %}
3
+ {% import 'admin/actions.html' as actionslib with context %}
4
+
5
+ {% block body %}
6
+ {% block breadcrums %}
7
+ <nav aria-label="breadcrumb">
8
+ <ol class="breadcrumb">
9
+ <li class="breadcrumb-item">
10
+ <a href="{{ get_dir_url('.index_view', path=None) }}">{{ _gettext('Root') }}</a>
11
+ </li>
12
+ {% for name, path in breadcrumbs[:-1] %}
13
+ <li class="breadcrumb-item">
14
+ <a href="{{ get_dir_url('.index_view', path=path) }}">{{ name }}</a>
15
+ </li>
16
+ {% endfor %}
17
+ {% if breadcrumbs %}
18
+ <li class="breadcrumb-item">
19
+ <a href="{{ get_dir_url('.index_view', path=breadcrumbs[-1][1]) }}">{{ breadcrumbs[-1][0] }}</a>
20
+ </li>
21
+ {% endif %}
22
+ </ol>
23
+ </nav>
24
+ {% endblock %}
25
+
26
+ {% block file_list_table %}
27
+ <div class="table-responsive">
28
+ <table class="table table-vcenter table-striped table-bordered model-list">
29
+ <thead>
30
+ <tr>
31
+ {% block list_header scoped %}
32
+ {% if actions %}
33
+ <th class="list-checkbox-column">
34
+ <input type="checkbox" name="rowtoggle" class="action-rowtoggle" />
35
+ </th>
36
+ {% endif %}
37
+ <th class="">&nbsp;</th>
38
+ {% for column in admin_view.column_list %}
39
+ <th>
40
+ {% if admin_view.is_column_sortable(column) %}
41
+ {% if sort_column == column %}
42
+ <a href="{{ sort_url(column, dir_path, True) }}" title="{{ _gettext('Sort by %(name)s', name=column) }}">
43
+ {{ admin_view.column_label(column) }}
44
+ {% if sort_desc %}
45
+ <span class="fa fa-chevron-up"></span>
46
+ {% else %}
47
+ <span class="fa fa-chevron-down"></span>
48
+ {% endif %}
49
+ </a>
50
+ {% else %}
51
+ <a href="{{ sort_url(column, dir_path) }}" title="{{ _gettext('Sort by %(name)s', name=column) }}">{{ admin_view.column_label(column) }}</a>
52
+ {% endif %}
53
+ {% else %}
54
+ {{ _gettext(admin_view.column_label(column)) }}
55
+ {% endif %}
56
+ </th>
57
+ {% endfor %}
58
+ {% endblock %}
59
+ </tr>
60
+ </thead>
61
+ {% for name, path, is_dir, size, date in items %}
62
+ <tr>
63
+ {% block list_row scoped %}
64
+ {% if actions %}
65
+ <td>
66
+ {% if not is_dir %}
67
+ <input type="checkbox" name="rowid" class="action-checkbox" value="{{ path }}" />
68
+ {% endif %}
69
+ </td>
70
+ {% endif %}
71
+ <td>
72
+ {% block list_row_actions scoped %}
73
+ {% if admin_view.can_rename and path and name != '..' %}
74
+ {%- if admin_view.rename_modal -%}
75
+ {{ lib.add_modal_button(url=get_url('.rename', path=path, modal=True),
76
+ title=_gettext('Rename File'),
77
+ content='<i class="fa fa-pencil"></i>') }}
78
+ {% else %}
79
+ <a class="icon" href="{{ get_url('.rename', path=path) }}" title="{{ _gettext('Rename File') }}">
80
+ <i class="fa fa-pencil"></i>
81
+ </a>
82
+ {%- endif -%}
83
+ {% endif %}
84
+ {%- if admin_view.can_delete and path -%}
85
+ {% if is_dir %}
86
+ {% if name != '..' and admin_view.can_delete_dirs %}
87
+ <form class="icon" method="POST" action="{{ get_url('.delete') }}">
88
+ {{ delete_form.path(value=path) }}
89
+ {% if delete_form.csrf_token is defined and delete_form.csrf_token %}
90
+ {{ delete_form.csrf_token }}
91
+ {% endif %}
92
+ <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')">
93
+ <i class="fa fa-times"></i>
94
+ </button>
95
+ </form>
96
+ {% endif %}
97
+ {% else %}
98
+ <form class="icon" method="POST" action="{{ get_url('.delete') }}">
99
+ {{ delete_form.path(value=path) }}
100
+ {% if delete_form.csrf_token is defined and delete_form.csrf_token %}
101
+ {{ delete_form.csrf_token }}
102
+ {% endif %}
103
+ <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')">
104
+ <i class="fa fa-trash"></i>
105
+ </button>
106
+ </form>
107
+ {% endif %}
108
+ {%- endif -%}
109
+ {% endblock %}
110
+ </td>
111
+ {% if is_dir %}
112
+ <td colspan="2">
113
+ <a href="{{ get_dir_url('.index_view', path)|safe }}">
114
+ <i class="fa fa-folder-o"></i> <span>{{ name }}</span>
115
+ </a>
116
+ </td>
117
+ {% else %}
118
+ <td>
119
+ {% if admin_view.can_download %}
120
+ {%- if admin_view.edit_modal and admin_view.is_file_editable(path) -%}
121
+ {{ lib.add_modal_button(url=get_file_url(path, modal=True)|safe,
122
+ btn_class='', content=name) }}
123
+ {% else %}
124
+ <a href="{{ get_file_url(path)|safe }}">{{ name }}</a>
125
+ {%- endif -%}
126
+ {% else %}
127
+ {{ name }}
128
+ {% endif %}
129
+ </td>
130
+ {% if admin_view.is_column_visible('size') %}
131
+ <td>
132
+ {{ size|filesizeformat }}
133
+ </td>
134
+ {% endif %}
135
+ {% endif %}
136
+ {% if admin_view.is_column_visible('date') %}
137
+ <td>
138
+ {{ timestamp_format(date) }}
139
+ </td>
140
+ {% endif %}
141
+ {% endblock %}
142
+ </tr>
143
+ {% endfor %}
144
+ </table>
145
+ </div>
146
+ {% endblock %}
147
+ {% block toolbar %}
148
+ <div class="btn-toolbar">
149
+ {% if admin_view.can_upload %}
150
+ <div class="btn-group">
151
+ {%- if admin_view.upload_modal -%}
152
+ {{ lib.add_modal_button(url=get_dir_url('.upload', path=dir_path, modal=True),
153
+ btn_class="btn btn-secondary",
154
+ content=_gettext('Upload File')) }}
155
+ {% else %}
156
+ <a class="btn btn-secondary" href="{{ get_dir_url('.upload', path=dir_path) }}">{{ _gettext('Upload File') }}</a>
157
+ {%- endif -%}
158
+ </div>
159
+ {% endif %}
160
+ {% if admin_view.can_mkdir %}
161
+ <div class="ms-1">
162
+ {%- if admin_view.mkdir_modal -%}
163
+ {{ lib.add_modal_button(url=get_dir_url('.mkdir', path=dir_path, modal=True),
164
+ btn_class="btn btn-secondary",
165
+ content=_gettext('Create Directory')) }}
166
+ {% else %}
167
+ <a class="btn btn-secondary" href="{{ get_dir_url('.mkdir', path=dir_path) }}">{{ _gettext('Create Directory') }}</a>
168
+ {%- endif -%}
169
+ </div>
170
+ {% endif %}
171
+ {% if actions %}
172
+ <div class="ms-1">
173
+ {{ actionslib.dropdown(actions, 'dropdown-toggle btn btn-secondary') }}
174
+ </div>
175
+ {% endif %}
176
+ </div>
177
+ {% endblock %}
178
+
179
+ {% block actions %}
180
+ {{ actionslib.form(actions, get_url('.action_view')) }}
181
+ {% endblock %}
182
+
183
+ {%- if admin_view.rename_modal or admin_view.mkdir_modal
184
+ or admin_view.upload_modal or admin_view.edit_modal -%}
185
+ {{ lib.add_modal_window() }}
186
+ {%- endif -%}
187
+ {% endblock %}
188
+
189
+ {% block tail %}
190
+ {{ super() }}
191
+ {{ actionslib.script(_gettext('Please select at least one file.'),
192
+ actions,
193
+ actions_confirmation) }}
194
+ <script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='admin/js/bs4_modal.js', v='1.0.0') }}"></script>
195
+ {% endblock %}
@@ -0,0 +1,19 @@
1
+ {% import 'admin/static.html' as admin_static with context %}
2
+ {% import 'admin/lib.html' as lib with context %}
3
+
4
+ {% block body %}
5
+ {# content added to modal-content #}
6
+ <div class="modal-header">
7
+ {% block header %}<h3>{{ header_text }}</h3>{% endblock %}
8
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
9
+ </div>
10
+ <div class="modal-body">
11
+ {% block fa_form %}
12
+ {{ lib.render_form(form, dir_url, action=request.url, is_modal=True) }}
13
+ {% endblock %}
14
+ </div>
15
+ {% endblock %}
16
+
17
+ {% block tail %}
18
+ <script {{ admin_csp_nonce_attribute }} src="{{ admin_static.url(filename='admin/js/bs4_modal.js', v='1.0.0') }}"></script>
19
+ {% endblock %}
@@ -0,0 +1,4 @@
1
+ {% extends 'admin/master.html' %}
2
+
3
+ {% block body %}
4
+ {% endblock %}
@@ -0,0 +1,103 @@
1
+ {% macro menu_icon(item) -%}
2
+
3
+ {% if item %}
4
+ {% set icon_type = item.get_icon_type() %}
5
+ {%- if icon_type %}
6
+ {% set icon_value = item.get_icon_value() %}
7
+ {% if icon_type == 'glyph' %}
8
+ <i class="{{ icon_value }}"></i>
9
+ {% elif icon_type == 'fa' %}
10
+ <i class="fa {{ icon_value }}"></i>
11
+ {% elif icon_type == 'tabler' %}
12
+ <i class="ti ti-{{ icon_value }}"></i>
13
+ {% elif icon_type == 'image' %}
14
+ <img src="{{ url_for('static', filename=icon_value) }}" alt="menu image">
15
+ {% elif icon_type == 'image-url' %}
16
+ <img src="{{ icon_value }}" alt="menu image">
17
+ {% endif %}
18
+ {% endif %}
19
+ {% endif %}
20
+
21
+ {%- endmacro %}
22
+
23
+ {% macro menu(menu_root=None) %}
24
+ {% set is_main_nav = menu_root == None %}
25
+ {% if menu_root is none %}{% set menu_root = admin_view.admin.menu() %}{% endif %}
26
+ {%- for item in menu_root %}
27
+ {%- if item.is_category() -%}
28
+ {% set children = item.get_children() %}
29
+ {%- if children %}
30
+ {% set class_name = item.get_class_name() or '' %}
31
+
32
+ <li class="nav-item dropdown {% if item.is_active(admin_view) %}active{% endif %}">
33
+ <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="javascript:void(0)" role="button"
34
+ aria-haspopup="true" aria-expanded="false">
35
+ {% if item.class_name %}<span class="{{ item.class_name }}"></span> {% endif %}
36
+ {{ menu_icon(item) }}{{ item.name }}
37
+ </a>
38
+ <div class="dropdown-menu">
39
+ {%- for child in children -%}
40
+ {%- if child.is_category() -%}
41
+ {{ menu(menu_root=[child]) }}
42
+ {% else %}
43
+ {% set class_name = child.get_class_name() %}
44
+ {% if 'dropdown-divider' in class_name %}
45
+ <div class="dropdown-divider"></div>
46
+ {% else %}
47
+ {%- if child.is_active(admin_view) %}
48
+ <a class="dropdown-item active {{ class_name }}" href="{{ child.get_url() }}"{% if child.target %}
49
+ target="{{ child.target }}"{% endif %}>
50
+ {{ menu_icon(child) }}{{ child.name }}</a>
51
+ {% else %}
52
+ <a class="dropdown-item {{ class_name }}" href="{{ child.get_url() }}"{% if child.target %}
53
+ target="{{ child.target }}"{% endif %}>
54
+ {{ menu_icon(child) }}{{ child.name }}</a>
55
+ {%- endif %}
56
+ {%- endif %}
57
+ {%- endif %}
58
+ {%- endfor %}
59
+ </div>
60
+ </li>
61
+ {% endif %}
62
+ {%- else %}
63
+ {%- if item.is_accessible() and item.is_visible() -%}
64
+ {% set class_name = item.get_class_name() %}
65
+ <li class="nav-item {% if item.is_active(admin_view) %}active{% endif %}">
66
+ <a class="nav-link {{ class_name }}" href="{{ item.get_url() }}"{% if item.target %} target="{{ item.target }}"{% endif %}>
67
+ {{ menu_icon(item) }}{{ item.name }}</a>
68
+ </li>
69
+ {%- endif -%}
70
+ {% endif -%}
71
+ {% endfor %}
72
+ {% endmacro %}
73
+
74
+ {% macro menu_links(links=None) %}
75
+ {% if links is none %}{% set links = admin_view.admin.menu_links() %}{% endif %}
76
+ {% for item in links %}
77
+ {% set class_name = item.get_class_name() %}
78
+ {% if item.is_accessible() and item.is_visible() %}
79
+ <li class="nav-item">
80
+ <a class="nav-link {{ class_name }}" href="{{ item.get_url() }}"{% if item.target %} target="{{ item.target }}"{% endif %}>
81
+ {{ menu_icon(item) }}{{ item.name }}</a>
82
+ </li>
83
+ {% endif %}
84
+ {% endfor %}
85
+ {% endmacro %}
86
+
87
+ {% macro messages() %}
88
+ {% with messages = get_flashed_messages(with_categories=True) %}
89
+ {% if messages %}
90
+ {% for category, m in messages %}
91
+ {% if category %}
92
+ {% set mapping = {'message': 'info', 'error': 'danger'} %}
93
+ <div class="alert alert-{{ mapping.get(category, category) }} alert-dismissible fade show" role="alert">
94
+ {% else %}
95
+ <div class="alert alert-dismissible fade show" role="alert">
96
+ {% endif %}
97
+ {{ m }}
98
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
99
+ </div>
100
+ {% endfor %}
101
+ {% endif %}
102
+ {% endwith %}
103
+ {% endmacro %}