flask-construct-ui 0.12.1__tar.gz → 0.12.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/PKG-INFO +1 -1
  2. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/grid.py +9 -0
  3. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/schema.py +67 -0
  4. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/css/whitelabel.css +6 -0
  5. flask_construct_ui-0.12.2/flask_construct_ui/static/js/prosemirror-bundle.min.js +35 -0
  6. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/_macros.html +10 -3
  7. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/listindex.html +5 -2
  8. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/related_crud_tab_list.html +5 -2
  9. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui.egg-info/PKG-INFO +1 -1
  10. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/pyproject.toml +1 -1
  11. flask_construct_ui-0.12.1/flask_construct_ui/static/js/prosemirror-bundle.min.js +0 -34
  12. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/LICENSE +0 -0
  13. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/README.md +0 -0
  14. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/__init__.py +0 -0
  15. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/app_hooks.py +0 -0
  16. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/auth.py +0 -0
  17. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/base.py +0 -0
  18. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/capabilities.py +0 -0
  19. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/cli.py +0 -0
  20. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/__init__.py +0 -0
  21. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/calendar.py +0 -0
  22. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/chart.py +0 -0
  23. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/chart_native.py +0 -0
  24. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/content.py +0 -0
  25. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/core.py +0 -0
  26. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/grid.py +0 -0
  27. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/kanban.py +0 -0
  28. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/layout.py +0 -0
  29. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/live.py +0 -0
  30. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/period.py +0 -0
  31. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/components/pivot.py +0 -0
  32. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/contrast.py +0 -0
  33. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/crud.py +0 -0
  34. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/crud_forms.py +0 -0
  35. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/csrf.py +0 -0
  36. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/data.py +0 -0
  37. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/data_sqlalchemy.py +0 -0
  38. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/dragdrop.py +0 -0
  39. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/errors.py +0 -0
  40. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/flashes.py +0 -0
  41. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/forms.py +0 -0
  42. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/headless.py +0 -0
  43. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/icons.py +0 -0
  44. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/l10n/en/main.flt +0 -0
  45. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/l10n/sv/main.flt +0 -0
  46. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/language.py +0 -0
  47. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/layout.py +0 -0
  48. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/live.py +0 -0
  49. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/menu.py +0 -0
  50. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/navbar.py +0 -0
  51. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/navigation.py +0 -0
  52. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/orgmode.py +0 -0
  53. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/pagination.py +0 -0
  54. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/py.typed +0 -0
  55. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/rcrud.py +0 -0
  56. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/resume.py +0 -0
  57. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/sanitize.py +0 -0
  58. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/css/grid.css +0 -0
  59. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/css/prosemirror-bundle.min.css +0 -0
  60. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/css/unpoly.min.css +0 -0
  61. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/designs/adminlte.css +0 -0
  62. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/designs/sandstone.css +0 -0
  63. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/fonts/bootstrap-icons/LICENSE.txt +0 -0
  64. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/fonts/bootstrap-icons/bootstrap-icons.min.css +0 -0
  65. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/fonts/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  66. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/fonts/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  67. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/fonts/material-symbols/LICENSE.txt +0 -0
  68. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/fonts/material-symbols/material-symbols-outlined.woff2 +0 -0
  69. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/fonts/material-symbols/material-symbols.css +0 -0
  70. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/fonts/material-symbols/names.txt +0 -0
  71. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/js/construct-core.min.js +0 -0
  72. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/js/construct-grid.min.js +0 -0
  73. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/js/kanban-dnd.js +0 -0
  74. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/static/js/unpoly.min.js +0 -0
  75. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/_form_field.html +0 -0
  76. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/button.html +0 -0
  77. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/calendar.html +0 -0
  78. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/calendarmini.html +0 -0
  79. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/card.html +0 -0
  80. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/chart.html +0 -0
  81. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/chart_native.html +0 -0
  82. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/form.html +0 -0
  83. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/grid.html +0 -0
  84. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/grid_layout.html +0 -0
  85. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/grid_notice.html +0 -0
  86. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/horizontal_layout.html +0 -0
  87. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/kanban.html +0 -0
  88. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/live.html +0 -0
  89. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/live_refresh.html +0 -0
  90. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/main.html +0 -0
  91. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/markup.html +0 -0
  92. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/period_nav.html +0 -0
  93. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/pivot_form.html +0 -0
  94. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/rich_text.html +0 -0
  95. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/table.html +0 -0
  96. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/tabs.html +0 -0
  97. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/text.html +0 -0
  98. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/components/vertical_layout.html +0 -0
  99. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/_many_related_options.html +0 -0
  100. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/_related_options.html +0 -0
  101. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/create.html +0 -0
  102. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/edit.html +0 -0
  103. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/related_crud_tab_form.html +0 -0
  104. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/tab_view.html +0 -0
  105. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/crud/view.html +0 -0
  106. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/errors/error.html +0 -0
  107. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/errors/forbidden.html +0 -0
  108. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/navbar/notifications.html +0 -0
  109. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/navbar/search_results.html +0 -0
  110. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/structure/base.html +0 -0
  111. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/structure/public.html +0 -0
  112. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/style/_flashes.html +0 -0
  113. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/style/_footer.html +0 -0
  114. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/style/_menu.html +0 -0
  115. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/style/base_full.html +0 -0
  116. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/templates/style/base_partial.html +0 -0
  117. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/testing.py +0 -0
  118. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/urls.py +0 -0
  119. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui/validation.py +0 -0
  120. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui.egg-info/SOURCES.txt +0 -0
  121. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui.egg-info/dependency_links.txt +0 -0
  122. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui.egg-info/requires.txt +0 -0
  123. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/flask_construct_ui.egg-info/top_level.txt +0 -0
  124. {flask_construct_ui-0.12.1 → flask_construct_ui-0.12.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flask-construct-ui
3
- Version: 0.12.1
3
+ Version: 0.12.2
4
4
  Summary: A UI toolkit for Flask with CRUD and component features.
5
5
  Author-email: Olof Tjerngren <olof@tjerngren.net>
6
6
  License-Expression: LGPL-3.0-or-later
@@ -36,6 +36,8 @@ import decimal
36
36
  import enum
37
37
  from typing import Any
38
38
 
39
+ import markupsafe
40
+
39
41
  from . import schema as schema_mod
40
42
  from .data import query as data_query
41
43
  from .forms import validate_form_data
@@ -365,6 +367,13 @@ def cell_text(field, name, row):
365
367
  """The text a cell displays. Always plain text — never markup: a cell
366
368
  holds user data, and rendering it as markup would be stored XSS on every
367
369
  page that embeds the grid."""
370
+ if field.has_display_filter():
371
+ value = field.display_value(name, row, "grid")
372
+ if isinstance(value, markupsafe.Markup):
373
+ # A filter's markup is meant for HTML surfaces; a grid cell keeps
374
+ # only its text.
375
+ return value.striptags()
376
+ return "" if value is None else str(value)
368
377
  if field.field_type == "bool":
369
378
  return "✓" if getattr(row, name, None) else ""
370
379
  value = field.format_value(name, row)
@@ -3,6 +3,27 @@ import datetime
3
3
  from .validation import ValidationError, parse_rule
4
4
 
5
5
 
6
+ class DisplayContext:
7
+ """What a display filter gets alongside the value it is formatting.
8
+
9
+ One object rather than more positional arguments, so later additions
10
+ don't break filters apps have already written.
11
+
12
+ ``surface`` says where the value is being shown: ``"list"`` (the CRUD
13
+ list), ``"related"`` (a related-records tab), ``"view"`` (a read-only
14
+ field on the view or edit page), ``"inline"`` (a row of a list field) or
15
+ ``"grid"`` (a data grid cell, which is plain text only).
16
+ """
17
+
18
+ __slots__ = ("row", "name", "field", "surface")
19
+
20
+ def __init__(self, row, name, field, surface):
21
+ self.row = row
22
+ self.name = name
23
+ self.field = field
24
+ self.surface = surface
25
+
26
+
6
27
  class SchemaField:
7
28
  def __init__(self, field_type, name, **kwargs):
8
29
  self.field_type = field_type
@@ -26,6 +47,8 @@ class SchemaField:
26
47
  # only replaces the generic "Enter <field>" hint and vanishes on input.
27
48
  self.placeholder = kwargs.get("placeholder", None)
28
49
  self.help = kwargs.get("help", None)
50
+ # Display-only filter, ``fn(value, ctx)`` — see ``display_value``.
51
+ self.display = kwargs.get("display", None)
29
52
  self.kwargs = kwargs
30
53
  matches = kwargs.get("matches", None)
31
54
  self._matches_fn = None
@@ -61,8 +84,52 @@ class SchemaField:
61
84
  return 'required aria-required="true"' if self.required else ""
62
85
 
63
86
  def format_value(self, field, row):
87
+ """The stored value, as a form control or editor is filled with it.
88
+
89
+ This feeds ``value="…"`` attributes as well as read-only text, so it
90
+ must never return markup. To change only how a value *reads*, use a
91
+ display filter — see ``display_value``.
92
+ """
64
93
  return getattr(row, field)
65
94
 
95
+ def _display_filter(self):
96
+ if self.display is not None:
97
+ return self.display
98
+ from flask import has_app_context, current_app
99
+
100
+ if has_app_context():
101
+ filters = current_app.config.get("CONSTRUCT_DISPLAY_FILTERS") or {}
102
+ return filters.get(self.field_type)
103
+ return None
104
+
105
+ def has_display_filter(self):
106
+ """Whether ``display_value`` differs from ``format_value`` here.
107
+
108
+ Templates check this before their built-in per-type rendering (check
109
+ icons, badges, …), so a filter can restyle any field type, while
110
+ fields without one render exactly as before.
111
+ """
112
+ if type(self).display_value is not SchemaField.display_value:
113
+ return True
114
+ return self._display_filter() is not None
115
+
116
+ def display_value(self, field, row, surface="list"):
117
+ """The value as it is shown — never typed — in lists, related tabs,
118
+ read-only views and grid cells.
119
+
120
+ Resolved, most specific first: the field's ``display=`` callable, the
121
+ app's ``CONSTRUCT_DISPLAY_FILTERS[field_type]``, then ``format_value``
122
+ unchanged. A filter is called as ``fn(value, ctx)`` with the
123
+ ``format_value`` result and a :class:`DisplayContext`. It may return
124
+ ``markupsafe.Markup`` for intentional HTML; anything else is escaped.
125
+ Subclasses may override this for full control.
126
+ """
127
+ value = self.format_value(field, row)
128
+ fn = self._display_filter()
129
+ if fn is None:
130
+ return value
131
+ return fn(value, DisplayContext(row, field, self, surface))
132
+
66
133
  def parse_value(self, value):
67
134
  return value
68
135
 
@@ -820,6 +820,12 @@ a:hover {
820
820
  padding: var(--fcu-table-cell-padding-y) var(--fcu-table-cell-padding-x);
821
821
  border-bottom: 1px solid var(--fcu-border);
822
822
  }
823
+ /* List cells carry fcu-cell--<field_type> and data-field="<name>", so an app
824
+ can style a column without a display filter. A date breaking at its hyphens
825
+ in a narrow column reads as two values. */
826
+ .fcu-cell--date, .fcu-cell--datetime {
827
+ white-space: nowrap;
828
+ }
823
829
  .fcu-table th {
824
830
  background-color: var(--fcu-table-header-bg);
825
831
  font-weight: var(--fcu-font-weight-semibold);