accrete 0.0.143__py3-none-any.whl → 0.0.145__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 (39) hide show
  1. accrete/contrib/log/admin.py +1 -1
  2. accrete/contrib/system_mail/tasks.py +4 -5
  3. accrete/contrib/ui/__init__.py +15 -14
  4. accrete/contrib/ui/filter.py +22 -13
  5. accrete/contrib/ui/response.py +353 -0
  6. accrete/contrib/ui/static/css/accrete.css +69 -12
  7. accrete/contrib/ui/static/css/accrete.css.map +1 -1
  8. accrete/contrib/ui/static/css/accrete.scss +370 -300
  9. accrete/contrib/ui/templates/ui/content.html +0 -0
  10. accrete/contrib/ui/templates/ui/content_right.html +2 -2
  11. accrete/contrib/ui/templates/ui/detail.html +11 -0
  12. accrete/contrib/ui/templates/ui/filter/filter.html +5 -5
  13. accrete/contrib/ui/templates/ui/filter/query_tags.html +1 -1
  14. accrete/contrib/ui/templates/ui/layout.html +69 -64
  15. accrete/contrib/ui/templates/ui/list.html +21 -19
  16. accrete/contrib/ui/templates/ui/list_update.html +9 -3
  17. accrete/contrib/ui/templates/ui/message.html +1 -1
  18. accrete/contrib/ui/templates/ui/modal.html +41 -32
  19. accrete/contrib/ui/templates/ui/table_row_update.html +2 -2
  20. accrete/contrib/ui/templates/ui/templatetags/field.html +19 -6
  21. accrete/contrib/ui/templates/ui/widgets/model_search_select.html +7 -7
  22. accrete/contrib/ui/templates/ui/widgets/model_search_select_multi.html +3 -3
  23. accrete/contrib/ui/templatetags/ui.py +24 -2
  24. accrete/contrib/user/templates/user/password_forgotten.html +1 -0
  25. accrete/contrib/user/templates/user/user_preferences.html +43 -0
  26. accrete/contrib/user/views.py +36 -37
  27. accrete/migrations/0008_alter_member_access_groups_and_more.py +23 -0
  28. accrete/utils/__init__.py +1 -0
  29. accrete/utils/forms.py +11 -3
  30. accrete/utils/views.py +1 -2
  31. accrete/views.py +2 -3
  32. {accrete-0.0.143.dist-info → accrete-0.0.145.dist-info}/METADATA +2 -2
  33. {accrete-0.0.143.dist-info → accrete-0.0.145.dist-info}/RECORD +35 -33
  34. accrete/contrib/ui/context.py +0 -123
  35. accrete/contrib/ui/static/css/.sass-cache/15adf1eed05371361b08787c918a7f18fc15be79/accrete.scssc +0 -0
  36. accrete/contrib/ui/utils.py +0 -88
  37. accrete/contrib/user/templates/user/user_form.html +0 -58
  38. {accrete-0.0.143.dist-info → accrete-0.0.145.dist-info}/WHEEL +0 -0
  39. {accrete-0.0.143.dist-info → accrete-0.0.145.dist-info}/licenses/LICENSE +0 -0
@@ -44,4 +44,4 @@ class LogAdmin(admin.ModelAdmin):
44
44
 
45
45
 
46
46
  admin.site.register(models.LogConfig, LogConfigAdmin)
47
- admin.site.register(models.Log, LogAdmin)
47
+ admin.site.register(models.Log, LogAdmin)
@@ -36,8 +36,7 @@ def run_mail_queue():
36
36
  _logger.error(f'Failed to send system mail\n{error_str}')
37
37
  email.error = error_str
38
38
  email.save()
39
- continue
40
-
41
- email.sent = True
42
- email.error = None
43
- email.save()
39
+ else:
40
+ email.sent = True
41
+ email.error = None
42
+ email.save()
@@ -1,18 +1,19 @@
1
1
  from . import widgets
2
2
  from .filter import Filter
3
- from . context import (
4
- Context,
5
- ListContext,
6
- ListUpdateContext,
7
- TableContext,
8
- TableRowContext,
9
- ModalContext,
10
- OobContext,
11
- MessageContext
12
- )
13
- from .utils import (
14
- modal_response,
15
- detail_response,
3
+ from .response import (
4
+ Response,
5
+ WindowResponse,
6
+ ListResponse,
7
+ ListEntryResponse,
8
+ TableResponse,
9
+ TableRowResponse,
10
+ DetailResponse,
11
+ ModalResponse,
12
+ OobResponse,
13
+ TriggerResponse,
14
+ ClientTrigger,
16
15
  search_select_response,
17
- add_trigger
16
+ message_response,
17
+ add_trigger,
18
+ update
18
19
  )
@@ -46,12 +46,21 @@ class Filter:
46
46
  TYPES_TIME = ['TimeField']
47
47
 
48
48
  def __init__(
49
- self, model, query: QueryDict, default_lookup: str = None
49
+ self,
50
+ model,
51
+ query_dict: QueryDict,
52
+ default_lookup: str = None,
53
+ query: Q = None,
54
+ select_related: list[str] = None,
55
+ prefetch_related: list[str] = None
50
56
  ):
51
57
  self.model = model
52
- self.query = query
58
+ self.query_dict = query_dict
53
59
  self.model_name = f'{self.model._meta.app_label}.{self.model._meta.model_name}'
54
- seen_models = query.get('models', '').split(',')
60
+ self.query = query or Q()
61
+ self.select_related = select_related
62
+ self.prefetch_related = prefetch_related
63
+ seen_models = query_dict.get('models', '').split(',')
55
64
  self.seen_models = [
56
65
  model for model in seen_models
57
66
  if model and model != self.model_name
@@ -67,14 +76,14 @@ class Filter:
67
76
  ) -> paginator.Page:
68
77
  return page_from_querystring(
69
78
  self.model,
70
- self.query,
71
- select_related=select_related,
72
- prefetch_related=prefetch_related,
73
- query=query
79
+ self.query_dict,
80
+ select_related=select_related or self.select_related,
81
+ prefetch_related=prefetch_related or self.prefetch_related,
82
+ query=query or self.query
74
83
  )
75
84
 
76
85
  def get_queryset(self) -> QuerySet:
77
- return filter_from_querystring(self.model, self.query)
86
+ return filter_from_querystring(self.model, self.query_dict)
78
87
 
79
88
  def query_params(self):
80
89
  fields = filter(
@@ -82,7 +91,7 @@ class Filter:
82
91
  self.model._meta.get_fields()
83
92
  )
84
93
  params = []
85
- path = self.query.get('path', '')
94
+ path = self.query_dict.get('path', '')
86
95
  for field in fields:
87
96
  field_params = self._get_field_params(field)
88
97
  if not field.is_relation or field_params['model_name'] not in self.seen_models:
@@ -115,7 +124,7 @@ class Filter:
115
124
  if not lookup:
116
125
  ctx = {
117
126
  'verbose_lookup': str(_('Select an attribute')),
118
- 'query_dict': self.query,
127
+ 'query_dict': self.query_dict,
119
128
  'model_name': self.model_name
120
129
  }
121
130
  return render_to_string('ui/filter/query_input.html', ctx)
@@ -133,14 +142,14 @@ class Filter:
133
142
  'lookup': prefix + lookup,
134
143
  'input': input_params,
135
144
  'lookup_operator': lookup_operator,
136
- 'query_dict': self.query,
145
+ 'query_dict': self.query_dict,
137
146
  'model_name': self.model_name
138
147
  }
139
148
  return render_to_string('ui/filter/query_input.html', ctx)
140
149
 
141
150
  def query_tags(self, query: list | dict = None, operator: str = None):
142
151
  if not query:
143
- query = json.loads(self.query.get('q', '[]'))
152
+ query = json.loads(self.query_dict.get('q', '[]'))
144
153
  if isinstance(query, dict):
145
154
  query = [query]
146
155
  operator = operator or '&'
@@ -291,7 +300,7 @@ class Filter:
291
300
  return str(labels[lookup])
292
301
 
293
302
  def _get_field_params(self, field):
294
- path = self.query.get('path', '')
303
+ path = self.query_dict.get('path', '')
295
304
  label = ''
296
305
  name = field.name
297
306
  if path:
@@ -0,0 +1,353 @@
1
+ import re
2
+ import ast
3
+ import json
4
+ from dataclasses import dataclass
5
+
6
+ from django.core import paginator
7
+ from django.db.models import Model
8
+ from django.http import HttpResponse
9
+ from django.template.loader import render_to_string
10
+ from django.utils.translation import gettext_lazy as _
11
+ from accrete.contrib.ui import Filter
12
+
13
+
14
+ class Response:
15
+
16
+ def __init__(self, *, template: str, context: dict):
17
+ self.template = template
18
+ self.context = context
19
+
20
+ @staticmethod
21
+ def add_trigger(response):
22
+ pass
23
+
24
+ def render(self, request) -> str:
25
+ return render_to_string(
26
+ template_name=self.template, context=self.context, request=request
27
+ )
28
+
29
+ def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
30
+ extra_content = extra_content or ''
31
+ res = HttpResponse(content=(
32
+ self.render(request)
33
+ + render_to_string('ui/message.html', request=request)
34
+ + extra_content
35
+ ))
36
+ self.add_trigger(res)
37
+ if replace_body:
38
+ res.headers['HX-Retarget'] = 'body'
39
+ res.headers['HX-Reswap'] = 'innerHTML'
40
+ res.headers['HX-Push-Url'] = request.path
41
+ return res
42
+
43
+
44
+ class OobResponse(Response):
45
+
46
+ oob_template = 'ui/oob.html'
47
+
48
+ def __init__(self, *, template: str, context: dict, swap: str, tag: str = 'div'):
49
+ super().__init__(template=self.oob_template, context=context)
50
+ self.context.update({'oob': {
51
+ 'template': template,
52
+ 'swap': swap,
53
+ 'tag': tag
54
+ }})
55
+
56
+
57
+ class WindowResponse(Response):
58
+
59
+ base_template = 'ui/layout.html'
60
+
61
+ def __init__(
62
+ self, *,
63
+ title: str,
64
+ context: dict,
65
+ overview_template: str = None,
66
+ header_template: str = None,
67
+ panel_template: str = None,
68
+ is_centered: bool = False
69
+ ):
70
+ super().__init__(template=self.base_template, context=context)
71
+ self.panel_template = panel_template
72
+ if 'has_panel' not in self.context.keys():
73
+ self.context.update(has_panel=self._has_panel())
74
+ self.context.update({
75
+ 'title': title,
76
+ 'overview_template': overview_template,
77
+ 'header_template': header_template,
78
+ 'panel_template': panel_template,
79
+ 'is_centered': is_centered
80
+ })
81
+
82
+ def _has_panel(self):
83
+ return bool(self.panel_template)
84
+
85
+ def response(self, request, extra_content: str = None, replace_body: bool = True) -> HttpResponse:
86
+ return super().response(request, extra_content, replace_body)
87
+
88
+
89
+ class ListResponse(WindowResponse):
90
+
91
+ def __init__(
92
+ self, *,
93
+ title: str,
94
+ context: dict,
95
+ list_entry_template: str = None,
96
+ page: paginator.Page = None,
97
+ ui_filter: Filter = None,
98
+ endless_scroll: bool = True,
99
+ header_template: str = None,
100
+ panel_template: str = None,
101
+ column_count: int = 1,
102
+ column_height: str = '150px',
103
+ overview_template: str = 'ui/list.html'
104
+ ):
105
+ assert page is not None or ui_filter is not None, _(
106
+ 'Argument page or ui_filter must be supplied'
107
+ )
108
+ self.ui_filter = ui_filter
109
+ self.overview_template = overview_template
110
+ super().__init__(
111
+ title=title,
112
+ context=context,
113
+ overview_template=self.overview_template,
114
+ header_template=header_template,
115
+ panel_template=panel_template,
116
+ )
117
+ if ui_filter and not page:
118
+ page = ui_filter.get_page()
119
+ self.context.update({
120
+ 'list_entry_template': list_entry_template,
121
+ 'page': page,
122
+ 'ui_filter': ui_filter,
123
+ 'endless_scroll': endless_scroll,
124
+ 'column_count': column_count,
125
+ 'column_height': column_height
126
+ })
127
+
128
+ def _has_panel(self):
129
+ return bool(self.panel_template or self.ui_filter)
130
+
131
+ def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
132
+ return super().response(request, extra_content, replace_body)
133
+
134
+
135
+ class ListEntryResponse(Response):
136
+
137
+ base_template = 'ui/list_update.html'
138
+
139
+ def __init__(
140
+ self, *,
141
+ instance: Model,
142
+ list_entry_template: str,
143
+ context: dict = None,
144
+ page: paginator.Page,
145
+ is_new: bool,
146
+ column_count: int = 1,
147
+ column_height: str = '150px',
148
+ ):
149
+ self.page = page
150
+ super().__init__(template=self.base_template, context=context or {})
151
+ self.context.update({
152
+ 'instance': instance,
153
+ 'list_entry_template': list_entry_template,
154
+ 'is_new': is_new,
155
+ 'column_count': column_count,
156
+ 'column_height': column_height
157
+ })
158
+
159
+ def render(self, request) -> str:
160
+ res = super().render(request)
161
+ if self.page:
162
+ pagination_update = OobResponse(
163
+ template='ui/layout.html#pagination',
164
+ swap='innerHTML:#pagination',
165
+ context=dict(page=self.page)
166
+ ).render(request)
167
+ res += pagination_update
168
+ return res
169
+
170
+
171
+ class TableResponse(WindowResponse):
172
+
173
+ def __init__(
174
+ self, *,
175
+ title: str,
176
+ context: dict,
177
+ object_label: str,
178
+ fields: list[str],
179
+ footer: dict = None,
180
+ page: paginator.Page = None,
181
+ ui_filter: Filter = None,
182
+ endless_scroll: bool = True,
183
+ header_template: str = None,
184
+ panel_template: str = None,
185
+ overview_template: str = 'ui/table.html'
186
+ ):
187
+ assert page is not None or ui_filter is not None, _(
188
+ 'Argument page or ui_filter must be supplied'
189
+ )
190
+ self.ui_filter = ui_filter
191
+ self.overview_template = overview_template
192
+ super().__init__(
193
+ title=title,
194
+ context=context,
195
+ overview_template=self.overview_template,
196
+ header_template=header_template,
197
+ panel_template=panel_template
198
+ )
199
+ if ui_filter and not page:
200
+ page = ui_filter.get_page()
201
+ self.context.update({
202
+ 'page': page,
203
+ 'ui_filter': ui_filter,
204
+ 'endless_scroll': endless_scroll,
205
+ 'fields': fields,
206
+ 'object_label': object_label,
207
+ 'footer': footer
208
+ })
209
+
210
+ def _has_panel(self):
211
+ return bool(self.panel_template or self.ui_filter)
212
+
213
+ def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
214
+ return super().response(request, extra_content, replace_body)
215
+
216
+
217
+ class TableRowResponse(Response):
218
+
219
+ base_template = 'ui/table_row_update.html'
220
+
221
+ def __init__(
222
+ self, *,
223
+ instance: Model,
224
+ fields: list[str],
225
+ footer: dict = None,
226
+ page: paginator.Page = None,
227
+ ):
228
+ self.page = page
229
+ context = {
230
+ 'instance': instance,
231
+ 'fields': fields,
232
+ 'footer': footer,
233
+ }
234
+ super().__init__(template=self.base_template, context=context)
235
+
236
+ def render(self, request) -> str:
237
+ res = super().render(request)
238
+ if self.page:
239
+ pagination_update = OobResponse(
240
+ template='ui/layout.html#pagination',
241
+ swap='innerHTML:#pagination',
242
+ context=dict(page=self.page)
243
+ ).render(request)
244
+ res += pagination_update
245
+ return res
246
+
247
+
248
+ class DetailResponse(Response):
249
+
250
+ base_template = 'ui/detail.html'
251
+
252
+ def __init__(
253
+ self, *,
254
+ context: dict,
255
+ detail_header_template: str = None,
256
+ detail_data_template: str = None
257
+ ):
258
+ super().__init__(template=self.base_template, context=context)
259
+ self.context.update({
260
+ 'detail_header_template': detail_header_template,
261
+ 'detail_data_template': detail_data_template
262
+ })
263
+
264
+ @staticmethod
265
+ def add_trigger(response):
266
+ add_trigger(response, 'activate-detail')
267
+
268
+
269
+ class ModalResponse(Response):
270
+
271
+ def __init__(
272
+ self, *,
273
+ modal_id: str,
274
+ template: str,
275
+ context: dict,
276
+ title: str = None,
277
+ is_update: bool = False,
278
+ is_blocking: bool = False,
279
+ modal_width: str = None
280
+
281
+ ):
282
+ super().__init__(template=template, context=context)
283
+ self.context.update({
284
+ 'title': title,
285
+ 'modal_id': re.sub(r'[^A-Za-z-]+', '', modal_id).strip('-'),
286
+ 'is_update': is_update,
287
+ 'is_blocking': is_blocking,
288
+ 'modal_width': modal_width
289
+ })
290
+
291
+
292
+ @dataclass
293
+ class ClientTrigger:
294
+
295
+ trigger: dict | str
296
+ header: str = 'HX-Trigger'
297
+
298
+
299
+ class TriggerResponse:
300
+
301
+ def __init__(self, trigger: list[ClientTrigger]):
302
+ self.trigger = trigger
303
+
304
+ def response(self):
305
+ res = HttpResponse()
306
+ res.headers['HX-Reswap'] = 'None'
307
+ for trigger in self.trigger:
308
+ add_trigger(res, trigger.trigger, trigger.header)
309
+ return res
310
+
311
+
312
+ def search_select_response(queryset) -> HttpResponse:
313
+ return HttpResponse(render_to_string(
314
+ 'ui/widgets/model_search_select_options.html',
315
+ {'options': queryset}
316
+ ))
317
+
318
+
319
+ def message_response(request, persistent: bool = False):
320
+ return HttpResponse(content=(render_to_string(
321
+ 'ui/message.html', context={'persistent': persistent}, request=request
322
+ )))
323
+
324
+
325
+ def add_trigger(
326
+ response: HttpResponse,
327
+ trigger: dict | str,
328
+ header: str = 'HX-Trigger'
329
+ ) -> HttpResponse:
330
+ if isinstance(trigger, str):
331
+ trigger = {trigger: ''}
332
+ res_trigger = response.headers.get(header)
333
+ if not res_trigger:
334
+ response.headers[header] = json.dumps(trigger)
335
+ return response
336
+ try:
337
+ res_trigger = ast.literal_eval(response.headers.get(header, '{}'))
338
+ except SyntaxError:
339
+ res_trigger = {response.headers[header]: ''}
340
+ res_trigger.update(trigger)
341
+ response.headers[header] = json.dumps(res_trigger)
342
+ return response
343
+
344
+
345
+ def update(request, ui_responses: list[Response]) -> HttpResponse:
346
+ response = HttpResponse()
347
+ content = ''
348
+ for res in ui_responses:
349
+ content += res.render(request)
350
+ res.add_trigger(response)
351
+ content += render_to_string('ui/message.html', request=request)
352
+ response.content = content
353
+ return response
@@ -3,11 +3,12 @@
3
3
  --bulma-body-size: .9em;
4
4
  --bulma-navbar-height: 40px;
5
5
  --bulma-menu-item-selected-h: --bulma-success-h;
6
- --accrete-content-right-width: 500px;
6
+ --accrete-detail-width: 500px;
7
7
  --accrete-action-panel-width: 320px;
8
8
  --bulma-input-icon-focus-color: var(--bulma-success);
9
9
  --bulma-input-arrow: var(--bulma-success);
10
10
  --bulma-arrow-color: var(--bulma-success);
11
+ --bulma-menu-nested-list-margin: .5em 0 0.5em 0.75em;
11
12
  --bulma-navbar-burger-color: var(--bulma-success);
12
13
  --accrete-hover-color: #F0F2F4; }
13
14
 
@@ -18,10 +19,12 @@ html[data-theme='light'] {
18
19
  --bulma-menu-item-background-l: 50%;
19
20
  --bulma-menu-item-color-l: 10%;
20
21
  --bulma-navbar-burger-color: var(--bulma-success);
22
+ --bulma-input-disabled-border-color: var(--bulma-input-border-color) !important;
21
23
  --accrete-hover-color: #F0F2F4; }
22
24
 
23
25
  html[data-theme='dark'] {
24
26
  --bulma-navbar-burger-color: var(--bulma-success);
27
+ --bulma-input-disabled-border-color: var(--bulma-input-border-color) !important;
25
28
  --accrete-hover-color: #1E2128; }
26
29
  html[data-theme='dark'] .button.is-light {
27
30
  --bulma-button-h: var(--bulma-dark-h);
@@ -45,12 +48,18 @@ a {
45
48
  color: inherit;
46
49
  text-decoration: inherit; }
47
50
 
51
+ .label {
52
+ font-weight: normal; }
53
+
48
54
  .navbar-burger {
49
55
  color: black; }
50
56
 
51
57
  .button.is-subtle {
52
58
  box-shadow: none;
53
- border: none; }
59
+ border-top-color: transparent;
60
+ border-right-color: transparent;
61
+ border-bottom-color: transparent;
62
+ border-left-color: transparent; }
54
63
 
55
64
  .input {
56
65
  border-top: none;
@@ -60,12 +69,20 @@ a {
60
69
  border-top-right-radius: 0;
61
70
  border-bottom-right-radius: 0;
62
71
  border-bottom-left-radius: 0;
63
- box-shadow: none; }
72
+ box-shadow: none;
73
+ font-weight: bold; }
64
74
 
65
75
  .input:focus, .input:focus-within {
66
76
  box-shadow: none;
67
77
  border-bottom: 1px solid var(--bulma-success); }
68
78
 
79
+ input[disabled] {
80
+ background-color: transparent !important;
81
+ cursor: unset !important; }
82
+
83
+ input[disabled] ~ .helptext {
84
+ font-weight: normal !important; }
85
+
69
86
  textarea {
70
87
  border-top: transparent !important;
71
88
  border-right: transparent !important;
@@ -94,15 +111,16 @@ select {
94
111
  border-right: transparent !important;
95
112
  border-left: transparent !important;
96
113
  border-radius: 0 !important;
97
- box-shadow: none !important; }
114
+ box-shadow: none !important;
115
+ font-weight: bold; }
98
116
 
99
117
  select:focus {
100
118
  box-shadow: none;
101
119
  border-bottom: 1px solid var(--bulma-success) !important; }
102
120
 
103
- #content-right-container {
121
+ #detail-container {
104
122
  flex-shrink: 0;
105
- width: var(--accrete-content-right-width);
123
+ width: var(--accrete-detail-width);
106
124
  border-top-left-radius: var(--bulma-radius-medium);
107
125
  border-bottom-left-radius: var(--bulma-radius-medium);
108
126
  box-shadow: -2px 0 20px 0 var(--bulma-grey-light);
@@ -111,8 +129,8 @@ select:focus {
111
129
  .responsive-heading {
112
130
  display: none; }
113
131
 
114
- #content-left-container {
115
- container-name: content-left-container;
132
+ #overview-container {
133
+ container-name: overview-container;
116
134
  container-type: inline-size; }
117
135
 
118
136
  .htmx-indicator {
@@ -124,6 +142,24 @@ select:focus {
124
142
  .modal-request-overlay.htmx-request {
125
143
  pointer-events: auto; }
126
144
 
145
+ #detail-indicator {
146
+ position: fixed;
147
+ background: rgba(47, 47, 62, 0.2);
148
+ height: 100%;
149
+ width: 100%;
150
+ z-index: -100; }
151
+
152
+ #detail-indicator.htmx-request {
153
+ z-index: 999;
154
+ transition-delay: 300ms;
155
+ transition-property: opacity; }
156
+
157
+ #detail-indicator > .progress {
158
+ position: fixed;
159
+ top: 50%;
160
+ margin-left: 3%;
161
+ max-width: calc(var(--accrete-detail-width) - 6%); }
162
+
127
163
  .list-entry > .box:hover, .box.selected {
128
164
  outline: 2px solid var(--bulma-success); }
129
165
 
@@ -135,7 +171,11 @@ select:focus {
135
171
  font-weight: var(--bulma-weight-normal);
136
172
  line-height: 1.25; }
137
173
 
138
- @container content-left-container (max-width: 768px) {
174
+ .container {
175
+ padding-left: 0.5rem;
176
+ padding-right: 0.5rem; }
177
+
178
+ @container overview-container (max-width: 768px) {
139
179
  table.can-compact * td {
140
180
  width: 100%;
141
181
  float: left;
@@ -168,8 +208,11 @@ select:focus {
168
208
 
169
209
  .list-entry {
170
210
  height: fit-content !important; } }
211
+ @media screen and (min-width: 769px) {
212
+ .modal-card {
213
+ max-width: 100%; } }
171
214
  @media screen and (max-width: 768px) {
172
- #content-right-container {
215
+ #detail-container {
173
216
  flex-grow: 1;
174
217
  flex-shrink: 1;
175
218
  width: auto;
@@ -177,7 +220,16 @@ select:focus {
177
220
  border-bottom-left-radius: 0; }
178
221
 
179
222
  .level > .level-item.box {
180
- width: 100%; } }
223
+ width: 100%; }
224
+
225
+ .modal {
226
+ padding-top: 0 !important;
227
+ padding-bottom: 0 !important;
228
+ justify-content: center !important; }
229
+
230
+ .modal-card {
231
+ width: 100% !important;
232
+ max-height: 100svh; } }
181
233
  #action-panel {
182
234
  max-width: var(--accrete-action-panel-width);
183
235
  min-width: var(--accrete-action-panel-width);
@@ -188,7 +240,7 @@ select:focus {
188
240
  #action-panel {
189
241
  position: absolute;
190
242
  background: var(--bulma-scheme-main);
191
- z-index: 999;
243
+ z-index: 40;
192
244
  box-shadow: 2px 0 20px 0 var(--bulma-grey-light); } }
193
245
  .menu-list a.is-active, .menu-list a.is-selected, .menu-list button.is-active, .menu-list button.is-selected, .menu-list .menu-item.is-active, .menu-list .menu-item.is-selected {
194
246
  --bulma-menu-item-h: var(--bulma-success-h);
@@ -235,4 +287,9 @@ input[type=number] {
235
287
  padding-left: 0;
236
288
  border-left: none !important; }
237
289
 
290
+ .header-items > * {
291
+ margin-right: 0.4rem;
292
+ margin-bottom: 0.5rem;
293
+ margin-left: 0.4rem; }
294
+
238
295
  /*# sourceMappingURL=accrete.css.map */