accrete 0.0.9__py3-none-any.whl → 0.0.11__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.
@@ -1,4 +1,3 @@
1
- from . import config
2
1
  from .filter import Filter
3
2
  from .helper import (
4
3
  ClientAction,
@@ -6,7 +5,7 @@ from .helper import (
6
5
  FormContext,
7
6
  ListContext,
8
7
  BreadCrumb,
8
+ TableField,
9
9
  build_querystring,
10
- parse_client_actions,
11
10
  queryset_from_querystring
12
11
  )
@@ -8,11 +8,10 @@ from django.utils.translation import gettext_lazy as _
8
8
  from django.core.exceptions import FieldDoesNotExist
9
9
  from django.core.paginator import Paginator
10
10
  from accrete.contrib.ui.filter import Filter
11
- from . import config
12
11
 
13
12
  _logger = logging.getLogger(__name__)
14
13
 
15
- ACTIONS_TEMPLATE = config.ACCRETE_UI_ACTIONS_TEMPLATE
14
+ DEFAULT_PAGINATE_BY = 40
16
15
 
17
16
 
18
17
  @dataclass
@@ -25,18 +24,6 @@ class ClientAction:
25
24
  submit: bool = False
26
25
  form_id: str = 'form'
27
26
  class_list: list = field(default_factory=list)
28
- mobile: bool = True
29
- tablet: bool = True
30
-
31
-
32
- def parse_client_actions(actions: list[ClientAction], actions_template: str = None):
33
- tablet = (not all(action.tablet for action in actions))
34
- mobile = (not all(action.mobile for action in actions))
35
- return {
36
- 'actions_template': actions_template or ACTIONS_TEMPLATE,
37
- 'actions': actions,
38
- 'action_button': {'mobile': mobile, 'tablet': tablet}
39
- }
40
27
 
41
28
 
42
29
  @dataclass
@@ -46,6 +33,13 @@ class BreadCrumb:
46
33
  url: str
47
34
 
48
35
 
36
+ @dataclass
37
+ class TableField:
38
+
39
+ label: str
40
+ name: str
41
+
42
+
49
43
  @dataclass
50
44
  class ListContext:
51
45
 
@@ -57,12 +51,14 @@ class ListContext:
57
51
  extra_query: Q = None
58
52
  related_fields: list[str] = field(default_factory=list)
59
53
  prefetch_fields: list[str] = field(default_factory=list)
60
- paginate_by: int = 20
54
+ paginate_by: int = DEFAULT_PAGINATE_BY
61
55
  order_by: list[str] = None
62
56
  column_width: int = 12
63
57
  filter_relation_depth: int = 4
64
58
  actions: list[ClientAction] = field(default_factory=list)
65
59
  breadcrumbs: list[BreadCrumb] = field(default_factory=list)
60
+ obj_label: str = None
61
+ fields: list[TableField] = field(default_factory=list)
66
62
 
67
63
  def get_queryset(self):
68
64
  order = self.get_order()
@@ -116,10 +112,11 @@ class ListContext:
116
112
  'filter_terms': Filter(self.model, self.filter_relation_depth).get_query_terms(),
117
113
  'view_type': 'list',
118
114
  'breadcrumbs': self.breadcrumbs,
119
- 'querystring': build_querystring(self.get_params)
115
+ 'querystring': build_querystring(self.get_params),
116
+ 'actions': self.actions,
117
+ 'obj_label': self.obj_label or _('Name'),
118
+ 'fields': self.fields
120
119
  }
121
- if self.actions:
122
- context.update(parse_client_actions(self.actions))
123
120
  context.update(self.context)
124
121
  return context
125
122
 
@@ -130,14 +127,15 @@ class DetailContext:
130
127
  obj: Model
131
128
  get_params: dict
132
129
  order_by: str = None
133
- paginate_by: int = 20
130
+ paginate_by: int = DEFAULT_PAGINATE_BY
134
131
  title: str = None
135
132
  queryset: type[QuerySet] = None
136
133
  extra_query: Q = None
137
134
  related_fields: list[str] = field(default_factory=list)
138
135
  prefetch_fields: list[str] = field(default_factory=list)
139
136
  actions: list[ClientAction] = field(default_factory=list)
140
- breadcrumbs: list[BreadCrumb] = field(default_factory=list)
137
+ breadcrumbs: list[BreadCrumb] = field(default_factory=list),
138
+ context: dict = field(default_factory=dict)
141
139
 
142
140
  def get_queryset(self):
143
141
  order = self.get_order()
@@ -197,25 +195,26 @@ class DetailContext:
197
195
  'detail_pagination': False,
198
196
  'view_type': 'detail',
199
197
  'breadcrumbs': self.breadcrumbs,
200
- 'querystring': build_querystring(self.get_params, ['page'])
198
+ 'querystring': build_querystring(self.get_params, ['page']),
199
+ 'actions': self.actions
201
200
  }
202
201
  if self.paginate_by > 0:
203
202
  ctx.update(self.get_pagination_context())
204
- if self.actions:
205
- ctx.update(parse_client_actions(self.actions))
203
+ ctx.update(self.context)
206
204
  return ctx
207
205
 
208
206
 
209
207
  @dataclass
210
208
  class FormContext:
211
209
 
212
- model: Model|type[Model]
210
+ model: Model | type[Model]
213
211
  get_params: dict
214
212
  title: str = None
215
213
  context: dict = field(default_factory=dict)
216
214
  form_id: str = 'form'
217
215
  add_default_actions: bool = True
218
216
  discard_url: str = None
217
+ actions: list[ClientAction] = field(default_factory=list)
219
218
 
220
219
  def get_default_form_actions(self):
221
220
  actions = [
@@ -228,12 +227,11 @@ class FormContext:
228
227
  ]
229
228
  try:
230
229
  url = self.discard_url or self.model.get_absolute_url()
231
- except TypeError as e:
232
- _logger.exception(
230
+ except TypeError:
231
+ raise TypeError(
233
232
  'Supply the discard_url parameter if FormContext is called '
234
233
  'with a model class instead of an instance.'
235
234
  )
236
- raise e
237
235
  except AttributeError as e:
238
236
  _logger.error(
239
237
  'Supply the discard_url parameter if FormContext is '
@@ -264,10 +262,12 @@ class FormContext:
264
262
  'title': self.get_title(),
265
263
  'view_type': 'form',
266
264
  'form_id': self.form_id,
267
- 'querystring': build_querystring(self.get_params, ['page'])
265
+ 'querystring': build_querystring(self.get_params, ['page']),
266
+ 'actions': []
268
267
  }
269
268
  if self.add_default_actions:
270
- ctx.update(parse_client_actions(self.get_default_form_actions()))
269
+ ctx.update({'actions': self.get_default_form_actions()})
270
+ ctx['actions'].extend(self.actions)
271
271
  ctx.update(self.context)
272
272
  return ctx
273
273
 
@@ -276,6 +276,8 @@ def build_querystring(get_params: dict, extra_params: list[str] = None) -> str:
276
276
  querystring = f'?q={get_params.get("q", "[]")}'
277
277
  if paginate_by := get_params.get('paginate_by', False):
278
278
  querystring += f'&paginate_by={paginate_by}'
279
+ # if page := get_params.get('page', False):
280
+ # querystring += f'&page={page}'
279
281
  if order_by := get_params.get('order_by', False):
280
282
  querystring += f'&order_by={order_by}'
281
283
  for param in extra_params or []:
@@ -1,32 +1,9 @@
1
- .header-sticky {
2
- position: sticky;
3
- top: 0;
4
- background: white;
5
- }
6
-
7
- table {
8
- margin-bottom: 12px !important;
9
- }
10
-
11
- th {
12
- background-color: white;
13
- }
14
-
15
- /*tbody > tr:hover {*/
16
- /* background-color: #f5f5f5;*/
17
- /*}*/
18
-
19
- label {
20
- word-break: break-word;
21
- }
22
-
23
- .dropdown-link:hover {
24
- cursor: pointer;
25
- background-color: #f5f5f5;
1
+ .columns {
2
+ margin: 0;
26
3
  }
27
4
 
28
- .link-row:hover {
29
- cursor: pointer;
5
+ .columns:last-child {
6
+ margin-bottom: 0;
30
7
  }
31
8
 
32
9
  .main-columns {
@@ -34,34 +11,16 @@ label {
34
11
  height: 100vh;
35
12
  }
36
13
 
37
- .side-panel {
38
- height: 100%;
39
- padding: 0 4px 0 0;
40
- border-radius: 0;
41
- position: sticky;
42
- top: 0;
43
- }
44
-
45
- .side-panel > .panel {
46
- border-radius: 0;
47
- height: 100%;
48
- overflow-y: auto;
49
- position: sticky;
50
- top: 0;
51
- }
52
-
53
- .info-panel {
54
- padding: 4px;
55
- border-radius: 0;
14
+ .column {
15
+ padding: 0;
56
16
  }
57
17
 
58
- .info-panel > .panel {
59
- border-radius: 0;
60
- box-shadow: 0 0 0 0
18
+ .breadcrumb * a {
19
+ color: #0a0a0a;
61
20
  }
62
21
 
63
- .title {
64
- word-break: break-all;
22
+ .table td, .table th {
23
+ padding: .2rem .75rem .2rem 0;
65
24
  }
66
25
 
67
26
  td > a {
@@ -69,15 +28,7 @@ td > a {
69
28
  width:100%;
70
29
  }
71
30
 
72
- .notification {
73
- word-wrap: break-word
74
- }
75
-
76
- .list-link {
77
- color: #48c78e;
78
- }
79
-
80
- a:link:not(.list-link) {
31
+ a {
81
32
  color: #0a0a0a;
82
33
  }
83
34
 
@@ -85,39 +36,10 @@ a:visited {
85
36
  color: #0a0a0a;
86
37
  }
87
38
 
88
- .content-column {
89
- height: 100%;
90
- overflow-y: auto;
91
- }
92
-
93
- .content-column > table > thead th {
94
- position: sticky;
95
- top: 0;
96
- }
97
-
98
- .buttons > .panel-block {
99
- padding: .5rem 0 .5rem .5rem;
100
- }
101
-
102
- .level-item > .panel-block {
103
- padding-left: 2px;
104
- padding-right: 2px;
105
- }
106
-
107
- .level-item > .panel-block:first-child {
108
- padding-left: 0;
109
- }
110
-
111
- .panel-block:not(:last-child) {
39
+ .side-panel * .panel-block:not(:last-child) {
112
40
  border-bottom: none !important;
113
41
  }
114
42
 
115
- .container > .level {
116
- margin: 0;
117
- max-width: 100%;
118
- overflow-x: hidden;
119
- }
120
-
121
43
  .level * {
122
44
  max-width: 100%;
123
45
  }
@@ -126,10 +48,6 @@ a:visited {
126
48
  word-break: break-all;
127
49
  }
128
50
 
129
- .columns {
130
- margin: 0;
131
- }
132
-
133
51
  input:focus {
134
52
  outline: 1px solid #48c78e !important;
135
53
  border: 1px solid #48c78e !important;
@@ -218,8 +136,22 @@ select:focus {
218
136
  margin-bottom: 0;
219
137
  }
220
138
 
221
- .level-is-shrinkable .level-left,
222
- .level-is-shrinkable .level-item,
223
- .level-is-shrinkable .level-right {
224
- flex-shrink: 1;
225
- }
139
+ .label.has-field {
140
+ height: 100%;
141
+ display: flex;
142
+ flex-direction: column;
143
+ justify-content: end;
144
+ font-weight: 300;
145
+ word-break: break-all;
146
+ padding: 12px 12px 12px 0;
147
+ }
148
+
149
+ .label.is-required {
150
+ font-weight: 700;
151
+ }
152
+
153
+ @media screen and (max-width: 768px) {
154
+ .label.has-field {
155
+ padding: 12px 0 12px 0;
156
+ }
157
+ }
@@ -5,7 +5,6 @@
5
5
  <div class="columns is-desktop">
6
6
  {% block messages %}{% endblock %}
7
7
  <div class="column p-0 is-8-desktop">
8
- {% include 'ui/partials/header.html' %}
9
8
  <div>
10
9
  {% block detail_content %}{% endblock %}
11
10
  </div>
@@ -4,7 +4,6 @@
4
4
  {% block content %}
5
5
  <div class="columns is-desktop">
6
6
  <div class="column p-0 is-8-desktop">
7
- {% include 'ui/partials/header.html' %}
8
7
  <div>
9
8
  {% include 'ui/partials/form_errors.html' with show_field_errors=True %}
10
9
  {% block form %}{% endblock %}
@@ -3,7 +3,7 @@
3
3
  {% load i18n %}
4
4
  {% load accrete_ui %}
5
5
 
6
- <html lang="en">
6
+ <html lang="en" style="overflow: hidden">
7
7
 
8
8
  <head>
9
9
  {% block head %}
@@ -62,47 +62,79 @@
62
62
  {% endblock %}
63
63
 
64
64
  <div class="main-columns columns m-0 is-mobile">
65
- {% block main_columns %}
66
- {% block side_panel %}
67
- <div class="side-panel column is-3-widescreen is-2-fullhd is-hidden-touch is-hidden-desktop-only is-hidden-widescreen-only" style="height: 100%">
68
- <nav class="panel is-shadowless pb-5">
69
- {% if list_pagination %}
70
- <div class="panel-block">
71
- {% include 'ui/partials/pagination_list.html' %}
72
- </div>
73
- {% endif %}
74
- {% if detail_pagination %}
75
- <div class="panel-block">
76
- {% include 'ui/partials/pagination_detail.html' %}
77
- </div>
78
- {% endif %}
79
- <div id="panel-actions">
80
- {% block side_panel_items %}{% if actions and actions_template %}{% include actions_template with all=True %}{% endif %}{% endblock %}
65
+ <div class="side-panel column is-3-widescreen is-2-fullhd is-hidden-touch is-hidden-desktop-only is-hidden-widescreen-only p-0" style="height: 100%; position: sticky; top: 0">
66
+ <nav class="panel is-shadowless pb-5" style="height: 100%; overflow-y: auto; position: sticky; top: 0">
67
+ {% if list_pagination %}
68
+ <div class="panel-block pb-0">
69
+ {% include 'ui/partials/pagination_list.html' %}
70
+ </div>
71
+ {% endif %}
72
+ {% if detail_pagination %}
73
+ <div class="panel-block pb-0">
74
+ {% include 'ui/partials/pagination_detail.html' %}
75
+ </div>
76
+ {% endif %}
77
+ <div id="panel-actions">
78
+ {% for action in actions %}
79
+ <div class="panel-block pb-0">
80
+ {% if action.submit %}
81
+ <input class="button is-fullwidth {{ action.class_list|join:' ' }}"
82
+ type="submit" form={{ action.form_id }} value="{{ action.name }}"
83
+ {% for attr in action.attrs %}{{ attr }}{% endfor %}
84
+ >
85
+ {% else %}
86
+ <a class="button is-fullwidth {{ action.class_list|join:' ' }}"
87
+ href="{{ action.url }}{{ querystring|default_if_none:'?' }}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
88
+ {% for attr in action.attrs %}{{ attr }}{% endfor %}
89
+ >{{ action.name }}
90
+ </a>
91
+ {% endif %}
81
92
  </div>
82
- {% if filter_terms %}
83
- <div class="panel-list px-3 has-text-centered">
84
- <span>{% translate 'Filter' %}</span>
85
- </div>
86
- <div class="panel-block" style="position: sticky">
87
- <button id="reset-filter-button"
88
- class="button is-fullwidth mr-1">{% translate 'Reset' %}
89
- </button>
90
- <button id="apply-filter-button"
91
- class="button is-fullwidth ml-1">{% translate 'Filter' %}
92
- </button>
93
- </div>
94
- <div id="filter-panel">
95
- {% include 'ui/partials/filter.html' %}
96
- </div>
97
- {% endif %}
98
- </nav>
93
+ {% endfor %}
99
94
  </div>
100
- {% endblock %}
95
+ {% if filter_terms %}
96
+ <div id="filter-panel" class="mt-2">
97
+ <div class="panel-list px-3 has-text-centered">
98
+ <span>{% translate 'Filter' %}</span>
99
+ </div>
100
+ <div class="panel-block" style="position: sticky">
101
+ <button id="reset-filter-button"
102
+ class="button is-fullwidth mr-1">{% translate 'Reset' %}
103
+ </button>
104
+ <button id="apply-filter-button"
105
+ class="button is-fullwidth ml-1">{% translate 'Filter' %}
106
+ </button>
107
+ </div>
108
+ {% include 'ui/partials/filter.html' %}
109
+ </div>
110
+ {% endif %}
111
+ </nav>
112
+ </div>
101
113
 
102
- <div id="content" class="column content-column p-0" style="height: 100%; overflow-y: auto">
103
- {% block content %}{% endblock %}
114
+ <div class="column" style="overflow-x: auto">
115
+ <div class="is-flex is-flex-direction-column" style="height: 100%; overflow: hidden">
116
+ <div>
117
+ {% include 'ui/partials/header.html' %}
118
+ </div>
119
+ <div id="content" class="px-3 pb-4" style="overflow: auto; flex-grow: 1">
120
+ {% block messages %}{% endblock %}
121
+ {% block content %}{% endblock %}
122
+ </div>
123
+ <div>
124
+ {% if list_pagination or detail_pagination %}
125
+ <div class="level is-hidden-tablet is-align-self-flex-start m-3">
126
+ <div class="level-item is-align-content-flex-start">
127
+ {% if list_pagination %}
128
+ {% include 'ui/partials/pagination_list.html' %}
129
+ {% elif detail_pagination %}
130
+ {% include 'ui/partials/pagination_detail.html' %}
131
+ {% endif %}
132
+ </div>
133
+ </div>
134
+ {% endif %}
135
+ </div>
104
136
  </div>
105
- {% endblock %}
137
+ </div>
106
138
  </div>
107
139
 
108
140
  {% if filter_terms %}
@@ -126,22 +158,5 @@
126
158
  </div>
127
159
  {% endif %}
128
160
 
129
- {% if actions %}
130
- <div id="action-modal" class="modal">
131
- <div class="modal-background action-modal-close"></div>
132
- <div class="modal-card">
133
- <header class="modal-card-head">
134
- <p class="modal-card-title">
135
- {% block action_modal_title %}{% translate 'Actions' %}{% endblock %}</p>
136
- <button class="delete action-modal-close" aria-label="close"></button>
137
- </header>
138
- <section class="modal-card-body">
139
- <div id="modal-actions">
140
- {% block action_modal_body %}{% if actions_template %}{% include actions_template with all=True %}{% endif %}{% endblock %}
141
- </div>
142
- </section>
143
- </div>
144
- </div>
145
- {% endif %}
146
161
  <div id="form-modal" class="modal"></div>
147
162
  </body>
@@ -8,27 +8,15 @@
8
8
  {% endblock %}
9
9
 
10
10
  {% block content %}
11
- <div id="list" class="columns">
12
- <div class="column p-0">
13
- {% include 'ui/partials/header.html' %}
14
- <div class="columns py-0">
15
- <div class="column is-12 py-0">
16
- {% block messages %}{% endblock %}
11
+ <div class="columns is-multiline">
12
+ {% for obj in page %}
13
+ <div class="column is-{{ column_width }}">
14
+ <div class="box p-2 pb-4 mb-1" style="word-break: break-word; height: 100%; border: solid #ccc 1px">
15
+ {% block object_data %}
16
+ {{ obj }}
17
+ {% endblock %}
17
18
  </div>
18
19
  </div>
19
- <div style="margin-left: -8px; margin-right: 8px">
20
- <div class="columns is-multiline pl-1 mb-5" style="max-width: 100%">
21
- {% for obj in page %}
22
- <div class="column is-{{ column_width }} mb-0 pr-0 pb-1 pt-0">
23
- <div class="box p-2 pb-4 mb-1" style="word-break: break-word; height: 100%; border: solid #ccc 1px">
24
- {% block object_data %}
25
- {{ obj }}
26
- {% endblock %}
27
- </div>
28
- </div>
29
- {% endfor %}
30
- </div>
31
- </div>
32
- </div>
20
+ {% endfor %}
33
21
  </div>
34
22
  {% endblock %}
@@ -1,18 +1,7 @@
1
1
  {% load i18n %}
2
2
 
3
- <div style="position: sticky; top: 0; background: white; z-index: 10">
4
- {% if list_pagination or detail_pagination %}
5
- <div class="level is-hidden-tablet is-hidden-fullhd is-align-self-flex-start pt-3 mx-2 mb-0">
6
- <div class="level-item is-align-content-flex-start">
7
- {% if list_pagination %}
8
- {% include 'ui/partials/pagination_list.html' %}
9
- {% elif detail_pagination %}
10
- {% include 'ui/partials/pagination_detail.html' %}
11
- {% endif %}
12
- </div>
13
- </div>
14
- {% endif %}
15
- <div class="level pt-4 px-3 level-is-shrinkable is-flex {% if detail_pagination %}is-mobile {% endif %}{% if list_pagination %}mb-1 {% else %}mb-0{% endif %}">
3
+ <div class="pt-3">
4
+ <div class="level level-is-shrinkable is-flex mb-2 pl-3 {% if detail_pagination %}is-mobile {% endif %}">
16
5
  <div class="level-left">
17
6
  <div class="level-item has-text-weight-bold">
18
7
  <nav class="breadcrumb" aria-label="breadcrumbs" style="white-space: unset; word-break: break-word">
@@ -26,7 +15,7 @@
26
15
  </div>
27
16
  </div>
28
17
  {% if list_pagination or detail_pagination %}
29
- <div class="level-right is-mobile is-hidden-mobile is-hidden-fullhd px-3 mb-1 is-align-self-flex-start">
18
+ <div class="level-right is-mobile is-hidden-mobile is-hidden-fullhd is-align-self-flex-start px-3">
30
19
  <div class="level-item">
31
20
  {% if list_pagination %}
32
21
  {% include 'ui/partials/pagination_list.html' %}
@@ -38,28 +27,27 @@
38
27
  {% endif %}
39
28
  </div>
40
29
 
41
- <div class="level p-0 m-0 is-mobile">
42
- <div class="level-left">
43
- <div class="level-item is-mobile is-hidden-tablet mr-0">
44
- <div class="buttons">
45
- {% block mobile_actions %}
46
- {% if actions_template %}{% include actions_template with mobile=True %}{% endif %}
47
- {% endblock %}
48
- </div>
49
- </div>
50
-
51
- <div class="level-item is-mobile is-hidden-fullhd is-hidden-mobile mr-0">
52
- <div class="buttons">
53
- {% block tablet_actions %}
54
- {% if actions_template %}{% include actions_template with tablet=True %}{% endif %}
55
- {% endblock %}
56
- </div>
57
- </div>
58
- <div class="level-item is-mobile is-hidden-fullhd is-hidden-mobile mr-0">
59
- <div class="buttons">
60
- <button id="modal-filter-button" class="button">Filter</button>
61
- </div>
62
- </div>
30
+ <div class="is-flex is-hidden-fullhd is-justify-content-space-between mb-2">
31
+ <div class="is-flex py-1 px-1 ml-2" style="overflow-x: auto">
32
+ {% for action in actions %}
33
+ {% if action.submit %}
34
+ <input class="button mr-2 {{ action.class_list|join:' ' }}"
35
+ type="submit" form={{ action.form_id }} value="{{ action.name }}"
36
+ {% for attr in action.attrs %}{{ attr }}{% endfor %}
37
+ >
38
+ {% else %}
39
+ <a class="button mr-2 {{ action.class_list|join:' ' }}"
40
+ href="{{ action.url }}{{ querystring|default_if_none:'?' }}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
41
+ {% for attr in action.attrs %}{{ attr }}{% endfor %}
42
+ >{{ action.name }}
43
+ </a>
44
+ {% endif %}
45
+ {% endfor %}
63
46
  </div>
47
+ {% if filter_terms %}
48
+ <div class="is-flex ml-2 py-1 pr-3">
49
+ <button id="modal-filter-button" class="button">Filter</button>
50
+ </div>
51
+ {% endif %}
64
52
  </div>
65
- </div>
53
+ </div>
@@ -3,24 +3,20 @@
3
3
  <button class="button"
4
4
  hx-get="{{ previous_object_url }}{{ querystring }}"
5
5
  hx-replace-url="true"
6
- hx-select-oob="#content,#detail-pagination"
6
+ hx-select-oob="#content,#detail-pagination,#panel-actions"
7
7
  ><
8
8
  </button>
9
9
 
10
10
  </p>
11
- {% if total_objects > 99999 %}
12
- <p class="control is-expanded">
13
- <button class="button is-fullwidth px-1"><span class="is-size-7">{{ current_object_idx }}<br>/{{ total_objects }}</span></button>
14
- </p>
15
- {% else %}
16
- <p class="control is-expanded">
17
- <button class="button is-fullwidth px-1">{{ current_object_idx }} / {{ total_objects }}</button>
18
- </p>
19
- {% endif %}
11
+ <p class="control is-expanded">
12
+ <button class="button is-fullwidth px-1" style="white-space: normal">
13
+ <span class="{% if total_objects > 99999 %}is-size-7{% endif %}">{{ current_object_idx }} / {{ total_objects }}</span>
14
+ </button>
15
+ </p>
20
16
  <p class="control">
21
17
  <button class="button" hx-get="{{ next_object_url }}{{ querystring }}"
22
18
  hx-replace-url="true"
23
- hx-select-oob="#content,#detail-pagination"
19
+ hx-select-oob="#content,#detail-pagination,#panel-actions"
24
20
  >>
25
21
  </button>
26
22
  </p>
@@ -4,25 +4,23 @@
4
4
  <button class="button"
5
5
  hx-get="{{ querystring }}&page={% if page.has_previous %}{{ page.previous_page_number }}{% else %}{{ paginator.num_pages }}{% endif %}"
6
6
  hx-replace-url="true"
7
- hx-select-oob="#content,#list-pagination"
7
+ hx-select-oob="#content,#list-pagination,#panel-actions"
8
8
  >
9
9
  <
10
10
  </button>
11
11
  </p>
12
12
  <p class="control is-expanded">
13
- {% if paginator.count > 9999 %}
14
- <button class="button is-fullwidth px-0">
15
- <span class="is-size-7">{{ page.start_index }}-{{ page.end_index }}<br>/{{ paginator.count }}</span>
16
- </button>
17
- {% else %}
18
- <button class="button is-fullwidth">{{ page.start_index }}-{{ page.end_index }}/{{ paginator.count }}</button>
19
- {% endif %}
13
+ <button class="button is-fullwidth px-1" style="white-space: normal">
14
+ <span class="{% if paginator.count > 9999 %}is-size-7{% endif %}">
15
+ {{ page.start_index }}-{{ page.end_index }} / {{ paginator.count }}
16
+ </span>
17
+ </button>
20
18
  </p>
21
19
  <p class="control">
22
20
  <button class="button"
23
21
  hx-get="{{ querystring }}&page={% if page.has_next %}{{ page.next_page_number }}{% else %}1{% endif %}"
24
22
  hx-replace-url="true"
25
- hx-select-oob="#content,#list-pagination"
23
+ hx-select-oob="#content,#list-pagination,#panel-actions"
26
24
  >
27
25
  >
28
26
  </button>
@@ -1,6 +1,7 @@
1
1
  {% extends 'ui/layout.html' %}
2
2
  {% load static %}
3
3
  {% load i18n %}
4
+ {% load accrete_ui %}
4
5
 
5
6
  {% block script %}
6
7
  {{ block.super }}
@@ -8,34 +9,32 @@
8
9
  {% endblock %}
9
10
 
10
11
  {% block content %}
11
- <div class="columns">
12
- <div class="column p-0">
13
- {% include 'ui/partials/header.html' %}
14
- <div class="columns py-0">
15
- <div class="column is-12 py-0">
16
- {% block messages %}{% endblock %}
17
- </div>
18
- </div>
19
- <div class="table-container">
20
- <table class="table is-fullwidth is-hoverable">
21
- <thead>
22
- {% block table_header_row %}
23
- <tr>
24
- {% block table_header %}{% endblock %}
25
- </tr>
26
- {% endblock %}
27
- </thead>
28
- <tbody>
29
- {% for obj in page %}
30
- {% block table_data_row %}
31
- <tr>
32
- {% block table_data %}{% endblock %}
33
- </tr>
34
- {% endblock %}
12
+ <table class="table is-fullwidth is-hoverable">
13
+ <thead style="position: sticky; top: 0; background: white; z-index: 10">
14
+ {% block table_header_row %}
15
+ <tr>
16
+ {% block table_header %}
17
+ <th>{{ obj_label }}</th>
18
+ {% for field in fields %}
19
+ <th>{{ field.label }}</th>
35
20
  {% endfor %}
36
- </tbody>
37
- </table>
38
- </div>
39
- </div>
40
- </div>
21
+ {% endblock %}
22
+ </tr>
23
+ {% endblock %}
24
+ </thead>
25
+ <tbody style="z-index: 9">
26
+ {% for obj in page %}
27
+ {% block table_data_row %}
28
+ <tr>
29
+ {% block table_data %}
30
+ <td><a class="is-underlined" href="{{ obj.get_absolute_url }}{{ querystring }}&page={{ page.number }}">{{ obj }}</a></td>
31
+ {% for field in fields %}
32
+ <td>{{ obj|get_attr:field.name|default_if_none:'---' }}</td>
33
+ {% endfor %}
34
+ {% endblock %}
35
+ </tr>
36
+ {% endblock %}
37
+ {% endfor %}
38
+ </tbody>
39
+ </table>
41
40
  {% endblock %}
@@ -1,8 +1,10 @@
1
+ import logging
1
2
  from django import template
2
3
  from django.conf import settings
3
4
  from django.template.loader import render_to_string
4
5
  from django.utils.safestring import mark_safe
5
6
 
7
+ _logger = logging.getLogger(__name__)
6
8
  register = template.Library()
7
9
 
8
10
 
@@ -17,6 +19,19 @@ def combine_templates(template_name):
17
19
  return mark_safe(html)
18
20
 
19
21
 
22
+ @register.filter(name='get_attr')
23
+ def get_attr_from_string(param, value):
24
+ try:
25
+ attr = getattr(param, value)
26
+ except AttributeError:
27
+ _logger.exception(f'Object {param} has no attribute {value}')
28
+ return ''
29
+ if callable(attr):
30
+ return attr()
31
+ else:
32
+ return attr
33
+
34
+
20
35
  @register.filter(name='render_query_params')
21
36
  def query_params_to_html(params):
22
37
  html = ''
@@ -2,7 +2,7 @@ from django.shortcuts import render
2
2
 
3
3
 
4
4
  def home(request):
5
- return render(request, 'ui/layout.html')
5
+ return render(request, 'ui/layout.html', {})
6
6
 
7
7
 
8
8
  def form_modal(request):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: accrete
3
- Version: 0.0.9
3
+ Version: 0.0.11
4
4
  Summary: Django Shared Schema Multi Tenant
5
5
  Author-email: Benedikt Jilek <benedikt.jilek@pm.me>
6
6
  License: Copyright (c) 2023 Benedikt Jilek
@@ -32,17 +32,16 @@ accrete/contrib/system_mail/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2
32
32
  accrete/contrib/system_mail/views.py,sha256=xc1IQHrsij7j33TUbo-_oewy3vs03pw_etpBWaMYJl0,63
33
33
  accrete/contrib/system_mail/migrations/0001_initial.py,sha256=6cwkkRXGjXvwXoMjjgmWmcPyXSTlUbhW1vMiHObk9MQ,1074
34
34
  accrete/contrib/system_mail/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- accrete/contrib/ui/__init__.py,sha256=KtdoYsDesEdQJCnlDaZ0iI-ss4JEQXdJhj_ROdFHgMs,238
35
+ accrete/contrib/ui/__init__.py,sha256=b0Aq41co9EVKRKKrsRyhHQsUHCqtbaeI90slAxSLgK0,207
36
36
  accrete/contrib/ui/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
37
37
  accrete/contrib/ui/apps.py,sha256=E0ao2ox6PQ3ldfeR17FXJUUJuGiWjm2DPCxHbPXGzls,152
38
- accrete/contrib/ui/config.py,sha256=X9cAYyJqIuE4bg5ja6N4WERHSu4HTkYEJpPvVZ4SwtA,206
39
38
  accrete/contrib/ui/filter.py,sha256=xTziJ7ySfbpwtaWAusJSkNUtaZbEeX9UKES2AKIuAak,11897
40
- accrete/contrib/ui/helper.py,sha256=i2-LGhqXDK3vPZ8eYkeliduGQG5o5vcvQXlxivx0h7k,13274
39
+ accrete/contrib/ui/helper.py,sha256=tQzUnBreoQcxv8t8a27Zmh6I7EewqmKZ7HzDO9gty0A,13235
41
40
  accrete/contrib/ui/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
42
41
  accrete/contrib/ui/urls.py,sha256=TUBlz_CGs9InTZoxM78GSnucA73I8knoh_obt12RUHM,186
43
- accrete/contrib/ui/views.py,sha256=vAktHp5a3E6qKX-7qbvRQGepQgN-_qUkLfeRP_pf9RA,187
42
+ accrete/contrib/ui/views.py,sha256=WpBKMsxFFG8eG4IN7TW_TPE6i3OFF7gnLDTK7JMKti8,191
44
43
  accrete/contrib/ui/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
- accrete/contrib/ui/static/css/accrete.css,sha256=Bl23ue2-iCeqx1S8MCbxUMOB6kwdQoD_3vPySTEcslM,3301
44
+ accrete/contrib/ui/static/css/accrete.css,sha256=nvU-3R0LJSclJMJ61pvQCRg1tWs07MpBRQIBNPPYBPg,2392
46
45
  accrete/contrib/ui/static/css/icons.css,sha256=-a9BJHrH5P02cfL5qZLZHZkyupqigWmbc5wAa0UEDrE,3035
47
46
  accrete/contrib/ui/static/icons/Logo.svg,sha256=hGZuxrAa-LRpFavFiF8Lnc7X9OQcqmb6Xl_dxx-27hM,1861
48
47
  accrete/contrib/ui/static/icons/accrete.svg,sha256=CWHJKIgk3hxL7xIaFSz2j1cK-eF1TroCbjcF58bgOIs,1024
@@ -57,21 +56,20 @@ accrete/contrib/ui/templates/django/forms/widgets/input.html,sha256=CRu81kTsbPwi
57
56
  accrete/contrib/ui/templates/django/forms/widgets/select.html,sha256=jT_UnHizHfdWTdJoSxjcIqTiR7NbVBA4bBSvkuDPRtw,394
58
57
  accrete/contrib/ui/templates/django/forms/widgets/text.html,sha256=MSmLlQc7PsPoDLVtTOOiWNprrsPriNr712yFxaHyDIo,47
59
58
  accrete/contrib/ui/templates/django/forms/widgets/textarea.html,sha256=c9BTedqb3IkXLyVYd0p9pR8DFnsXCNGoxVBWZTk_Fic,278
60
- accrete/contrib/ui/templates/ui/detail.html,sha256=Ae4RD61IO4Jx57aG05Ht6XBnVyPck8xnlsQ4Uv-Nz4w,562
61
- accrete/contrib/ui/templates/ui/form.html,sha256=HDR--6bLa6l9xFpcdX4NYnbI0jCk4u4bm-qg-2AgMXQ,457
62
- accrete/contrib/ui/templates/ui/layout.html,sha256=idzB2kP76grNeHhaeVkbO_rdYXv6ThGSHtEQp8fs7j4,6805
63
- accrete/contrib/ui/templates/ui/list.html,sha256=VbgzkeZyprbU7JnpyP31m9eL4G8MZpzKdAVe6BTmcik,1270
64
- accrete/contrib/ui/templates/ui/table.html,sha256=quvTy99RmX8uPGVCMdZa_w54bwmogNxBGy7sMnJPTxU,1386
65
- accrete/contrib/ui/templates/ui/partials/actions.html,sha256=urRu07sWxUBjdp9WgOpJnmf44ZHQDQXRae2QTdzV0lc,1342
59
+ accrete/contrib/ui/templates/ui/detail.html,sha256=m-CpLcoK-dL5jPlIY6Hzyq2EyEqcFZ5Bn-kB-T0Oqao,510
60
+ accrete/contrib/ui/templates/ui/form.html,sha256=HpcZCDU_ur_Zf5gpnH_F8OV6qfOYPl6Ecqg6w5RIayQ,405
61
+ accrete/contrib/ui/templates/ui/layout.html,sha256=aRC5-_co9EQiyGbtYFQ8mFDded7G-C2YnldAW4VOvQ8,7745
62
+ accrete/contrib/ui/templates/ui/list.html,sha256=HsmJAWDZU-qCPh4mrc8vGzkULbViG5fnLcmmp-NO2-4,671
63
+ accrete/contrib/ui/templates/ui/table.html,sha256=tbtccA6cbhF_ONis7Qm7S7_VK-rClddQl7fgxcO8VbU,1469
66
64
  accrete/contrib/ui/templates/ui/partials/filter.html,sha256=l_Vi3ZRepF5lpX_KENU4AyIELSeichsVr0g2KGHcjIQ,1844
67
65
  accrete/contrib/ui/templates/ui/partials/form_errors.html,sha256=1_TQvTdiejsn-43YSyp2YfnP52P-MFYb-HGY0DLm4oA,991
68
66
  accrete/contrib/ui/templates/ui/partials/form_modal.html,sha256=FFDfI5qjOCUBSGqDjBXa8tcqN2q94wOOCNFDaiyplHQ,1032
69
- accrete/contrib/ui/templates/ui/partials/header.html,sha256=L33nbExJduDQP8J8K0qQsoD4Y8qjQUOuwbJdrCBBf_w,3047
67
+ accrete/contrib/ui/templates/ui/partials/header.html,sha256=OhEINbE8ioFVEYZMFyqjfGTXCiFpse_gW6lvSL2amYk,2550
70
68
  accrete/contrib/ui/templates/ui/partials/onchange_form.html,sha256=K5twTGqRUW1iM2dGtdWntjsJvJVo5EIzKxX2HK-H1yw,160
71
- accrete/contrib/ui/templates/ui/partials/pagination_detail.html,sha256=9rQEaE31gU8sUFLireZ9iju78HucADBK1TQOb9nqw2I,1003
72
- accrete/contrib/ui/templates/ui/partials/pagination_list.html,sha256=MkIRMnCCUnK7Ksoy4SqXdMtnpqgHeCv5GyMN3tzNfCc,1232
69
+ accrete/contrib/ui/templates/ui/partials/pagination_detail.html,sha256=Mbqg8mOApBMnU-64lW-Z_H4v_PWd0UmuEiCWho5vUus,882
70
+ accrete/contrib/ui/templates/ui/partials/pagination_list.html,sha256=e4DOGaPa9fBrTFCwoW-UphNoJFlzrxFrU050bM1Cnbw,1146
73
71
  accrete/contrib/ui/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
- accrete/contrib/ui/templatetags/accrete_ui.py,sha256=mytEnz-a-Mw7EjhqybvTdRYQAi6RUBnzuoLjRT-T478,2113
72
+ accrete/contrib/ui/templatetags/accrete_ui.py,sha256=otBEHrMM4JuYtx9DuK52OE1YM5vwS8KGs8CGevGwBpE,2478
75
73
  accrete/contrib/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
74
  accrete/contrib/user/admin.py,sha256=YS4iApli7XUaIl9GsEJxys2j8sepX0by88omYHjff-E,85
77
75
  accrete/contrib/user/apps.py,sha256=oHDrAiHf-G57mZLyxqGJzRY2DbPprGFD-QgyVJG_ruI,156
@@ -103,7 +101,7 @@ accrete/migrations/0002_initial.py,sha256=dFOM7kdHlx7pVAh8cTDlZMtciN4O9Z547HAzEK
103
101
  accrete/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
102
  accrete/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
103
  accrete/utils/dates.py,sha256=XI58CqabLCC-Sg6qo5TPWh-pHuuZfDdGDU6KQeAMlGo,1066
106
- accrete-0.0.9.dist-info/METADATA,sha256=IL15guJBxntsuaxTZcq4onFXqzQH8hoYcRjG1wsroBw,4845
107
- accrete-0.0.9.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87
108
- accrete-0.0.9.dist-info/licenses/LICENSE,sha256=_7laeMIHnsd3Y2vJEXDYXq_PEXxIcjgJsGt8UIKTRWc,1057
109
- accrete-0.0.9.dist-info/RECORD,,
104
+ accrete-0.0.11.dist-info/METADATA,sha256=eAKqMGGLjjOGsbVzWBbIn9mS3ke6FEh7JsvTDIhaJEc,4846
105
+ accrete-0.0.11.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87
106
+ accrete-0.0.11.dist-info/licenses/LICENSE,sha256=_7laeMIHnsd3Y2vJEXDYXq_PEXxIcjgJsGt8UIKTRWc,1057
107
+ accrete-0.0.11.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- import logging
2
- from django.conf import settings
3
-
4
- _logger = logging.getLogger(__name__)
5
-
6
-
7
- ACCRETE_UI_ACTIONS_TEMPLATE = getattr(
8
- settings, 'ACCRETE_UI_ACTIONS_TEMPLATE',
9
- 'ui/partials/actions.html'
10
- )
@@ -1,30 +0,0 @@
1
- {% load i18n %}
2
-
3
- {% for action in actions %}
4
- {% if all or tablet and action.tablet or mobile and action.mobile %}
5
- <div class="panel-block">
6
- {% if action.template %}
7
- {% include action.template %}
8
- {% else %}
9
- {% if action.submit %}
10
- <input class="button is-fullwidth {{ action.class_list|join:' ' }}"
11
- type="submit" form={{ action.form_id }} value="{{ action.name }}"
12
- style="white-space: normal; height: auto"
13
- {{ action.attrs|join:' ' }}>
14
- {% else %}
15
- <a class="button is-fullwidth {{ action.class|join:' ' }}"
16
- href="{{ action.url }}{{ querystring|default_if_none:'?' }}{% if page %}&page={{ page.number }}{% endif %}{{ action.query_params }}"
17
- {% for attr in action.attrs %}{{ attr }}{% endfor %}
18
- style="word-break: break-word">{{ action.name }}
19
- </a>
20
- {% endif %}
21
- {% endif %}
22
- </div>
23
- {% endif %}
24
- {% endfor %}
25
-
26
- {% if tablet and action_button.tablet or mobile and action_button.mobile %}
27
- <div class="panel-block">
28
- <button class="button panel-block action-modal-button">{% translate 'Actions' %}</button>
29
- </div>
30
- {% endif %}