plain.admin 0.29.1__tar.gz → 0.31.0__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 (97) hide show
  1. {plain_admin-0.29.1 → plain_admin-0.31.0}/.gitignore +2 -0
  2. {plain_admin-0.29.1 → plain_admin-0.31.0}/PKG-INFO +1 -1
  3. plain_admin-0.31.0/plain/admin/assets/admin/admin.css +143 -0
  4. plain_admin-0.31.0/plain/admin/assets/admin/admin.js +89 -0
  5. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/assets/admin/list.js +19 -18
  6. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/assets/toolbar/toolbar.js +54 -55
  7. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/base.html +4 -4
  8. plain_admin-0.31.0/plain/admin/templates/elements/admin/CheckboxField.html +10 -0
  9. plain_admin-0.31.0/plain/admin/templates/elements/admin/InputField.html +8 -0
  10. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/Label.html +1 -1
  11. plain_admin-0.31.0/plain/admin/templates/elements/admin/SelectField.html +8 -0
  12. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/Submit.html +1 -1
  13. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/TextareaField.html +2 -0
  14. {plain_admin-0.29.1 → plain_admin-0.31.0}/pyproject.toml +1 -1
  15. plain_admin-0.29.1/plain/admin/assets/admin/admin.css +0 -141
  16. plain_admin-0.29.1/plain/admin/assets/admin/admin.js +0 -83
  17. plain_admin-0.29.1/plain/admin/templates/elements/admin/CheckboxField.html +0 -8
  18. plain_admin-0.29.1/plain/admin/templates/elements/admin/InputField.html +0 -6
  19. plain_admin-0.29.1/plain/admin/templates/elements/admin/SelectField.html +0 -6
  20. {plain_admin-0.29.1 → plain_admin-0.31.0}/LICENSE +0 -0
  21. {plain_admin-0.29.1 → plain_admin-0.31.0}/README.md +0 -0
  22. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/README.md +0 -0
  23. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/__init__.py +0 -0
  24. {plain_admin-0.29.1/plain/admin/assets/admin → plain_admin-0.31.0/plain/admin/assets/admin/vendor}/chart.js +0 -0
  25. {plain_admin-0.29.1/plain/admin/assets/admin → plain_admin-0.31.0/plain/admin/assets/admin/vendor}/jquery-3.6.1.slim.min.js +0 -0
  26. {plain_admin-0.29.1/plain/admin/assets/admin → plain_admin-0.31.0/plain/admin/assets/admin/vendor}/popper.min.js +0 -0
  27. {plain_admin-0.29.1/plain/admin/assets/admin → plain_admin-0.31.0/plain/admin/assets/admin/vendor}/tippy-bundle.umd.min.js +0 -0
  28. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/cards/__init__.py +0 -0
  29. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/cards/base.py +0 -0
  30. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/cards/charts.py +0 -0
  31. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/cards/tables.py +0 -0
  32. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/config.py +0 -0
  33. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/dates.py +0 -0
  34. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/default_settings.py +0 -0
  35. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/impersonate/README.md +0 -0
  36. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/impersonate/__init__.py +0 -0
  37. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/impersonate/middleware.py +0 -0
  38. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/impersonate/models.py +0 -0
  39. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/impersonate/permissions.py +0 -0
  40. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/impersonate/settings.py +0 -0
  41. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/impersonate/urls.py +0 -0
  42. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/impersonate/views.py +0 -0
  43. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/middleware.py +0 -0
  44. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/querystats/README.md +0 -0
  45. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/querystats/__init__.py +0 -0
  46. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/querystats/core.py +0 -0
  47. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/querystats/middleware.py +0 -0
  48. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/querystats/urls.py +0 -0
  49. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/querystats/views.py +0 -0
  50. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/cards/base.html +0 -0
  51. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/cards/card.html +0 -0
  52. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/cards/chart.html +0 -0
  53. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/cards/table.html +0 -0
  54. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/delete.html +0 -0
  55. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/detail.html +0 -0
  56. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/index.html +0 -0
  57. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/list.html +0 -0
  58. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/page.html +0 -0
  59. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/search.html +0 -0
  60. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/UUID.html +0 -0
  61. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/bool.html +0 -0
  62. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/datetime.html +0 -0
  63. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/default.html +0 -0
  64. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/dict.html +0 -0
  65. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/get_display.html +0 -0
  66. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/img.html +0 -0
  67. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/list.html +0 -0
  68. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/model.html +0 -0
  69. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/admin/values/queryset.html +0 -0
  70. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/Checkbox.html +0 -0
  71. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/FieldErrors.html +0 -0
  72. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/Help.html +0 -0
  73. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/Input.html +0 -0
  74. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/Select.html +0 -0
  75. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/elements/admin/Textarea.html +0 -0
  76. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/querystats/querystats.html +0 -0
  77. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/querystats/toolbar.html +0 -0
  78. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/toolbar/exception.html +0 -0
  79. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/toolbar/querystats.html +0 -0
  80. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/toolbar/request.html +0 -0
  81. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates/toolbar/toolbar.html +0 -0
  82. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/templates.py +0 -0
  83. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/toolbar.py +0 -0
  84. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/urls.py +0 -0
  85. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/views/__init__.py +0 -0
  86. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/views/base.py +0 -0
  87. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/views/models.py +0 -0
  88. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/views/objects.py +0 -0
  89. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/views/registry.py +0 -0
  90. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/views/types.py +0 -0
  91. {plain_admin-0.29.1 → plain_admin-0.31.0}/plain/admin/views/viewsets.py +0 -0
  92. {plain_admin-0.29.1 → plain_admin-0.31.0}/tests/app/settings.py +0 -0
  93. {plain_admin-0.29.1 → plain_admin-0.31.0}/tests/app/urls.py +0 -0
  94. {plain_admin-0.29.1 → plain_admin-0.31.0}/tests/app/users/migrations/0001_initial.py +0 -0
  95. {plain_admin-0.29.1 → plain_admin-0.31.0}/tests/app/users/migrations/__init__.py +0 -0
  96. {plain_admin-0.29.1 → plain_admin-0.31.0}/tests/app/users/models.py +0 -0
  97. {plain_admin-0.29.1 → plain_admin-0.31.0}/tests/test_admin.py +0 -0
@@ -13,3 +13,5 @@ plain*/tests/.plain
13
13
  .aider*
14
14
 
15
15
  /llms-full.txt
16
+
17
+ /.plain
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.admin
3
- Version: 0.29.1
3
+ Version: 0.31.0
4
4
  Summary: Admin dashboard and tools for Plain.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-Expression: BSD-3-Clause
@@ -0,0 +1,143 @@
1
+ /*
2
+ These standard CSS rules are intended to make it easier for the
3
+ end-user to drop in HTML elements and have a decent, consistent starting point.
4
+
5
+ We shouldn't use many custom classes in here (like .btn) because the user
6
+ could unintentionally overwrite those since our CSS is combined.
7
+ */
8
+ table {
9
+ width: 100%;
10
+ font-size: .875rem;
11
+ line-height: 1.25rem;
12
+ table-layout: auto;
13
+ }
14
+
15
+ th {
16
+ text-align: left;
17
+ padding: 0.5rem 0.5rem;
18
+ }
19
+
20
+ table th a {
21
+ color: rgba(255, 255, 255, 0.6);
22
+ }
23
+
24
+ table td a {
25
+ color: white;
26
+
27
+ &:hover {
28
+ text-decoration: none;
29
+ }
30
+ }
31
+
32
+ tbody tr:hover {
33
+ background-color: rgb(255, 255, 255, 0.1);
34
+ }
35
+
36
+ td {
37
+ padding: 0.5rem 0.5rem;
38
+ white-space: nowrap;
39
+ border-bottom: 1px solid rgb(255, 255, 255, 0.1);
40
+ }
41
+ tr:last-child td {
42
+ border-bottom: none;
43
+ }
44
+
45
+ table img {
46
+ height: 1.2rem;
47
+ border-radius: 2px;
48
+ }
49
+
50
+ main a {
51
+ color: rgb(37, 99, 235);
52
+ }
53
+
54
+ main a:hover {
55
+ text-decoration: underline;
56
+ }
57
+
58
+ select {
59
+ border-radius: 6px;
60
+ background-color: rgba(255, 255, 255, 0.05);
61
+ border: 1px solid rgba(255, 255, 255, 0.1);
62
+ }
63
+
64
+ .actions a,
65
+ .actions button,
66
+ main button {
67
+ display: inline-block;
68
+ padding: 8px 16px;
69
+ font-size: 14px;
70
+ font-weight: 500;
71
+ color: #ffffff;
72
+ text-align: center;
73
+ text-decoration: none;
74
+ background-color: #2a2826;
75
+ border: 1px solid #3f3d3b;
76
+ border-radius: 6px;
77
+ transition: background-color 0.2s, border-color 0.2s, transform 0.2s;
78
+ cursor: pointer;
79
+ flex-shrink: 0;
80
+
81
+ @media (max-width: 640px) {
82
+ font-size: 12px;
83
+ padding: 6px 12px;
84
+ }
85
+
86
+ &:hover {
87
+ background-color: #2a2928;
88
+ border-color: #504e4c;
89
+ transform: translateY(-1px);
90
+ }
91
+
92
+ &:active {
93
+ background-color: #141312;
94
+ border-color: #3f3d3b;
95
+ transform: translateY(0);
96
+ }
97
+
98
+ &:focus {
99
+ outline: 2px solid #e5e7eb;
100
+ outline-offset: 2px;
101
+ }
102
+ }
103
+
104
+ main button[type="submit"] {
105
+ background-color: #2563eb;
106
+ border-color: #2563eb;
107
+
108
+ &:hover {
109
+ background-color: #1d4ed8;
110
+ border-color: #1d4ed8;
111
+ }
112
+ &:focus {
113
+ outline: 2px solid #2563eb;
114
+ outline-offset: 2px;
115
+ }
116
+ }
117
+
118
+ /* Cards use these? */
119
+ section {
120
+ border: rgba(255, 255, 255, 0.1) 1px solid;
121
+ border-radius: 0.275rem;
122
+ overflow: auto;
123
+ background-color: rgba(255, 255, 255, 0.05);
124
+ color: rgba(255, 255, 255, 0.8);
125
+ display: flex;
126
+ flex-direction: column;
127
+ }
128
+
129
+ section > header {
130
+ padding: 0.5rem;
131
+ /* background-color: #FFFCF0; */
132
+ border-bottom: rgba(255, 255, 255, 0.05) 1px solid;
133
+ }
134
+
135
+ section > div {
136
+ padding: 0.75rem 0.5rem;
137
+ flex-grow: 1;
138
+ display: flex;
139
+ flex-direction: column;
140
+ align-items: center;
141
+ justify-content: center;
142
+ overflow: auto;
143
+ }
@@ -0,0 +1,89 @@
1
+ jQuery(($) => {
2
+ $("[data-toggle]").on("click", function (e) {
3
+ e.preventDefault();
4
+ const targets = $(this).data("toggle").split(",");
5
+ $.each(targets, (index, target) => {
6
+ const $target = $(target);
7
+ if ($target.data("toggle-class")) {
8
+ $target.toggleClass($target.data("toggle-class"));
9
+ } else {
10
+ $target.toggle();
11
+ }
12
+ });
13
+ });
14
+
15
+ $("[data-autosubmit]").on("change", function (e) {
16
+ $(this).closest("form").submit();
17
+ });
18
+
19
+ function createDropdowns(target) {
20
+ $(target)
21
+ .find("[data-dropdown]")
22
+ .each(function () {
23
+ const template = this.querySelector("template");
24
+ tippy(this, {
25
+ content: template.innerHTML,
26
+ trigger: "click",
27
+ allowHTML: true,
28
+ interactive: true,
29
+ duration: 100,
30
+ placement: "bottom-end",
31
+ offset: [0, 6],
32
+ arrow: false,
33
+ appendTo: () => document.body,
34
+ onCreate: (instance) => {
35
+ instance.popper.classList.add("*:bg-white/15");
36
+ instance.popper.classList.add("*:w-48");
37
+ instance.popper.classList.add("*:rounded-md");
38
+ instance.popper.classList.add("*:shadow-lg");
39
+ instance.popper.classList.add("*:ring-1");
40
+ instance.popper.classList.add("*:ring-white/20");
41
+ },
42
+ });
43
+ });
44
+ }
45
+
46
+ function createTooltips(target) {
47
+ $(target)
48
+ .find("[data-tooltip]")
49
+ .each(function () {
50
+ tippy(this, {
51
+ content: this.dataset.tooltip,
52
+ duration: 100,
53
+ });
54
+ });
55
+ }
56
+
57
+ function autolinkColumns(target) {
58
+ $(target)
59
+ .find("[data-column-autolink]")
60
+ .each(function () {
61
+ const $this = $(this);
62
+ if ($this.find("a").length > 0) {
63
+ // Column already has a link, so don't add another
64
+ return;
65
+ }
66
+ const autolinkUrl = $this.data("column-autolink");
67
+ if (!autolinkUrl) {
68
+ // No URL, so don't add a link
69
+ return;
70
+ }
71
+ const $link = $(document.createElement("a"));
72
+ $link.attr("href", autolinkUrl);
73
+ $link.addClass("flex p-2 -m-2 text-white/80 hover:no-underline");
74
+ $(this).wrapInner($link);
75
+ });
76
+ }
77
+
78
+ createDropdowns(document);
79
+ createTooltips(document);
80
+ autolinkColumns(document);
81
+
82
+ // Search uses htmx to load elements,
83
+ // so we need to hook those up too.
84
+ htmx.on("htmx:afterSwap", (evt) => {
85
+ createDropdowns(evt.detail.target);
86
+ createTooltips(evt.detail.target);
87
+ autolinkColumns(evt.detail.target);
88
+ });
89
+ });
@@ -1,12 +1,12 @@
1
- jQuery(function ($) {
2
- var $actionCheckbox = $("[data-action-checkbox]");
3
- var $actionPks = $('[name="action_pks"]');
4
- var $actionSelect = $('[name="action_name"]');
5
- var $actionSubmit = $('[data-actions-form] [type="submit"]');
6
- var $lastActionCheckboxChecked = null;
1
+ jQuery(($) => {
2
+ const $actionCheckbox = $("[data-action-checkbox]");
3
+ const $actionPks = $('[name="action_pks"]');
4
+ const $actionSelect = $('[name="action_name"]');
5
+ const $actionSubmit = $('[data-actions-form] [type="submit"]');
6
+ let $lastActionCheckboxChecked = null;
7
7
 
8
- $actionCheckbox.on("change", function () {
9
- var pks = [];
8
+ $actionCheckbox.on("change", () => {
9
+ const pks = [];
10
10
  $actionCheckbox.each(function () {
11
11
  if ($(this).is(":checked")) {
12
12
  pks.push($(this).attr("name"));
@@ -17,7 +17,7 @@ jQuery(function ($) {
17
17
  updateActionSubmit();
18
18
  });
19
19
 
20
- $actionSelect.on("change", function () {
20
+ $actionSelect.on("change", () => {
21
21
  updateActionSubmit();
22
22
  });
23
23
 
@@ -33,12 +33,12 @@ jQuery(function ($) {
33
33
  // Enable shift-clicking to select a range of checkboxes
34
34
  $actionCheckbox.on("click", function (e) {
35
35
  if (e.shiftKey) {
36
- var $this = $(this);
37
- var thisIndex = $actionCheckbox.index($this);
38
- var lastIndex = $actionCheckbox.index($lastActionCheckboxChecked);
39
- var minIndex = Math.min(thisIndex, lastIndex);
40
- var maxIndex = Math.max(thisIndex, lastIndex);
41
- var $checkboxes = $actionCheckbox.slice(minIndex, maxIndex + 1);
36
+ const $this = $(this);
37
+ const thisIndex = $actionCheckbox.index($this);
38
+ const lastIndex = $actionCheckbox.index($lastActionCheckboxChecked);
39
+ const minIndex = Math.min(thisIndex, lastIndex);
40
+ const maxIndex = Math.max(thisIndex, lastIndex);
41
+ const $checkboxes = $actionCheckbox.slice(minIndex, maxIndex + 1);
42
42
  $checkboxes.prop("checked", $this.is(":checked"));
43
43
  } else {
44
44
  $lastActionCheckboxChecked = $(this);
@@ -47,9 +47,10 @@ jQuery(function ($) {
47
47
 
48
48
  // Merge query params with the current url and the link url
49
49
  $("[data-merge-params]").each(function () {
50
- var currentUrl = new URL(window.location.href);
51
- var params = new URL($(this).attr("href"), window.location.href).searchParams;
52
- params.forEach(function (value, key) {
50
+ const currentUrl = new URL(window.location.href);
51
+ const params = new URL($(this).attr("href"), window.location.href)
52
+ .searchParams;
53
+ params.forEach((value, key) => {
53
54
  currentUrl.searchParams.set(key, value);
54
55
  });
55
56
  $(this).attr("href", currentUrl.toString());
@@ -1,8 +1,8 @@
1
1
  // Make this available to the JS console for the user
2
- var plainToolbar = {
2
+ const plainToolbar = {
3
3
  hide: function () {
4
4
  // Hide by inserting a style so it doesn't flash on page load
5
- var style = document.createElement("style");
5
+ const style = document.createElement("style");
6
6
  style.innerHTML = "#plaintoolbar { display: none; }";
7
7
  document.getElementsByTagName("head")[0].appendChild(style);
8
8
  this.stylesheet = style;
@@ -13,15 +13,14 @@ var plainToolbar = {
13
13
  this.stylesheet.remove();
14
14
  }
15
15
  },
16
- shouldHide: function () {
17
- var hiddenUntil = localStorage.getItem("plaintoolbar.hidden_until");
16
+ shouldHide: () => {
17
+ const hiddenUntil = localStorage.getItem("plaintoolbar.hidden_until");
18
18
  if (hiddenUntil) {
19
19
  if (Date.now() < hiddenUntil) {
20
20
  return true;
21
- } else {
22
- localStorage.removeItem("plaintoolbar.hidden_until");
23
- return false;
24
21
  }
22
+ localStorage.removeItem("plaintoolbar.hidden_until");
23
+ return false;
25
24
  }
26
25
  return false;
27
26
  },
@@ -32,33 +31,33 @@ var plainToolbar = {
32
31
  toggleExpand: function () {
33
32
  this.expanded = !this.expanded;
34
33
  document.querySelector("#plaintoolbar-details").classList.toggle("hidden");
35
- localStorage.setItem('plaintoolbar.expanded', this.expanded ? '1' : '0');
34
+ localStorage.setItem("plaintoolbar.expanded", this.expanded ? "1" : "0");
36
35
  },
37
36
  expand: function () {
38
37
  this.expanded = true;
39
38
  document.querySelector("#plaintoolbar-details").classList.remove("hidden");
40
- localStorage.setItem('plaintoolbar.expanded', '1');
39
+ localStorage.setItem("plaintoolbar.expanded", "1");
41
40
  },
42
41
  collapse: function () {
43
42
  this.expanded = false;
44
43
  document.querySelector("#plaintoolbar-details").classList.add("hidden");
45
- localStorage.setItem('plaintoolbar.expanded', '0');
44
+ localStorage.setItem("plaintoolbar.expanded", "0");
46
45
  },
47
46
  showTab: function (tabName) {
48
47
  this.expand();
49
48
 
50
- var toolbar = document.querySelector("#plaintoolbar");
51
- var tab = toolbar.querySelector("div[data-toolbar-tab=" + tabName + "]");
49
+ const toolbar = document.querySelector("#plaintoolbar");
50
+ const tab = toolbar.querySelector(`div[data-toolbar-tab=${tabName}]`);
52
51
 
53
52
  // If the tab doesn't exist for some reason, quit
54
53
  if (!tab) {
55
- console.warn("Toolbar tab " + tabName + " does not exist");
54
+ console.warn(`Toolbar tab ${tabName} does not exist`);
56
55
  return;
57
56
  }
58
57
 
59
58
  // Hide all children in the tab parent
60
- for (var i = 0; i < tab.parentNode.children.length; i++) {
61
- var child = tab.parentNode.children[i];
59
+ for (let i = 0; i < tab.parentNode.children.length; i++) {
60
+ const child = tab.parentNode.children[i];
62
61
  if (child !== tab) {
63
62
  child.style.display = "none";
64
63
  }
@@ -66,14 +65,14 @@ var plainToolbar = {
66
65
 
67
66
  tab.style.display = "block";
68
67
 
69
- toolbar.querySelectorAll("button[data-toolbar-tab]").forEach(function (tab) {
68
+ for (const tab of toolbar.querySelectorAll("button[data-toolbar-tab]")) {
70
69
  if (tab.dataset.toolbarTab === tabName) {
71
70
  tab.setAttribute("data-active", true);
72
71
  } else {
73
72
  tab.removeAttribute("data-active");
74
73
  }
75
- });
76
- localStorage.setItem('plaintoolbar.tab', tabName);
74
+ }
75
+ localStorage.setItem("plaintoolbar.tab", tabName);
77
76
  },
78
77
  };
79
78
 
@@ -82,85 +81,85 @@ if (plainToolbar.shouldHide()) {
82
81
  plainToolbar.hide();
83
82
  }
84
83
 
85
- window.addEventListener("load", function() {
84
+ window.addEventListener("load", () => {
86
85
  // Restore expanded/collapsed state
87
- var state = localStorage.getItem('plaintoolbar.expanded');
88
- if (state === '1') {
86
+ const state = localStorage.getItem("plaintoolbar.expanded");
87
+ if (state === "1") {
89
88
  plainToolbar.expand();
90
89
  // Restore last active tab
91
- var lastTab = localStorage.getItem('plaintoolbar.tab');
90
+ const lastTab = localStorage.getItem("plaintoolbar.tab");
92
91
  if (lastTab) {
93
92
  plainToolbar.showTab(lastTab);
94
93
  }
95
- } else if (state === '0') {
94
+ } else if (state === "0") {
96
95
  plainToolbar.collapse();
97
96
  }
98
- var toolbar = document.querySelector("#plaintoolbar");
97
+ const toolbar = document.querySelector("#plaintoolbar");
99
98
 
100
- toolbar.querySelectorAll("button[data-toolbar-tab]").forEach(function (tab) {
101
- tab.addEventListener("click", function () {
99
+ for (const tab of toolbar.querySelectorAll("button[data-toolbar-tab]")) {
100
+ tab.addEventListener("click", () => {
102
101
  plainToolbar.showTab(tab.dataset.toolbarTab);
103
102
  });
104
- });
103
+ }
105
104
 
106
- toolbar.querySelectorAll('[data-plaintoolbar-hide]').forEach(function(btn) {
107
- btn.addEventListener('click', function() {
105
+ for (const btn of toolbar.querySelectorAll("[data-plaintoolbar-hide]")) {
106
+ btn.addEventListener("click", () => {
108
107
  plainToolbar.hide();
109
108
  });
110
- });
109
+ }
111
110
 
112
- toolbar.querySelectorAll('[data-plaintoolbar-hideuntil]').forEach(function(btn) {
113
- btn.addEventListener('click', function() {
111
+ for (const btn of toolbar.querySelectorAll("[data-plaintoolbar-hideuntil]")) {
112
+ btn.addEventListener("click", () => {
114
113
  console.log("Hiding admin toolbar for 1 hour");
115
114
  plainToolbar.hideUntil(Date.now() + 3600000);
116
115
  });
117
- });
116
+ }
118
117
 
119
- toolbar.querySelectorAll('[data-plaintoolbar-expand]').forEach(function(btn) {
120
- btn.addEventListener('click', function() {
118
+ for (const btn of toolbar.querySelectorAll("[data-plaintoolbar-expand]")) {
119
+ btn.addEventListener("click", () => {
121
120
  plainToolbar.toggleExpand();
122
121
  });
123
- });
122
+ }
124
123
 
125
124
  // Enable manual resize of the expanded toolbar via drag handle
126
- var details = document.getElementById('plaintoolbar-details');
125
+ const details = document.getElementById("plaintoolbar-details");
127
126
  if (details) {
128
- var handle = details.querySelector('[data-resizer]');
129
- var content = handle.nextElementSibling;
130
- var isDragging = false;
131
- var startY = 0;
132
- var startHeight = 0;
127
+ const handle = details.querySelector("[data-resizer]");
128
+ const content = handle.nextElementSibling;
129
+ let isDragging = false;
130
+ let startY = 0;
131
+ let startHeight = 0;
133
132
  if (handle && content) {
134
133
  // Initial cursor
135
- handle.style.cursor = 'grab';
134
+ handle.style.cursor = "grab";
136
135
  // Start dragging
137
- handle.addEventListener('mousedown', function(e) {
136
+ handle.addEventListener("mousedown", (e) => {
138
137
  isDragging = true;
139
138
  startY = e.clientY;
140
139
  startHeight = content.offsetHeight;
141
- handle.style.cursor = 'grabbing';
140
+ handle.style.cursor = "grabbing";
142
141
  // Prevent text selection while dragging
143
- document.body.style.userSelect = 'none';
142
+ document.body.style.userSelect = "none";
144
143
  e.preventDefault();
145
144
  });
146
145
  // Handle dragging
147
- document.addEventListener('mousemove', function(e) {
146
+ document.addEventListener("mousemove", (e) => {
148
147
  if (!isDragging) return;
149
- var delta = e.clientY - startY;
148
+ const delta = e.clientY - startY;
150
149
  // Calculate new height: dragging up increases height
151
- var newHeight = startHeight - delta;
150
+ let newHeight = startHeight - delta;
152
151
  // Clamp between reasonable bounds
153
- var minHeight = 50;
154
- var maxHeight = window.innerHeight - 100;
152
+ const minHeight = 50;
153
+ const maxHeight = window.innerHeight - 100;
155
154
  newHeight = Math.max(minHeight, Math.min(maxHeight, newHeight));
156
- content.style.height = newHeight + 'px';
155
+ content.style.height = `${newHeight}px`;
157
156
  });
158
157
  // End dragging
159
- document.addEventListener('mouseup', function() {
158
+ document.addEventListener("mouseup", () => {
160
159
  if (isDragging) {
161
160
  isDragging = false;
162
- handle.style.cursor = 'grab';
163
- document.body.style.userSelect = '';
161
+ handle.style.cursor = "grab";
162
+ document.body.style.userSelect = "";
164
163
  }
165
164
  });
166
165
  }
@@ -13,11 +13,11 @@
13
13
  {% tailwind_css %}
14
14
  {% htmx_js %}
15
15
  <link href="{{ asset('admin/admin.css') }}" rel="stylesheet">
16
- <script src="{{ asset('admin/jquery-3.6.1.slim.min.js') }}"></script>
17
- <script src="{{ asset('admin/chart.js') }}" defer></script>
16
+ <script src="{{ asset('admin/vendor/jquery-3.6.1.slim.min.js') }}"></script>
17
+ <script src="{{ asset('admin/vendor/chart.js') }}" defer></script>
18
18
  <script src="{{ asset('admin/admin.js') }}" defer></script>
19
- <script src="{{ asset('admin/popper.min.js') }}" defer></script>
20
- <script src="{{ asset('admin/tippy-bundle.umd.min.js') }}" defer></script>
19
+ <script src="{{ asset('admin/vendor/popper.min.js') }}" defer></script>
20
+ <script src="{{ asset('admin/vendor/tippy-bundle.umd.min.js') }}" defer></script>
21
21
  {% block header_scripts %}{% endblock %}
22
22
  </head>
23
23
  <body class="flex min-h-screen bg-stone-950">
@@ -0,0 +1,10 @@
1
+ {% use_elements %}
2
+
3
+ <div>
4
+ <div class="flex items-center space-x-2">
5
+ <admin.Checkbox field={field} />
6
+ <admin.Label field={field}>{{ label }}</admin.Label>
7
+ {% if help is defined %}<admin.Help help={help} />{% endif %}
8
+ </div>
9
+ <admin.FieldErrors field={field} />
10
+ </div>
@@ -0,0 +1,8 @@
1
+ {% use_elements %}
2
+
3
+ <div class="space-y-1">
4
+ <admin.Label field={field}>{{ label }}</admin.Label>
5
+ <admin.Input field={field} />
6
+ {% if help is defined %}<admin.Help help={help} />{% endif %}
7
+ <admin.FieldErrors field={field} />
8
+ </div>
@@ -1,4 +1,4 @@
1
1
  <label for="{{ field.html_id }}" class="flex items-baseline justify-between w-full">
2
- <div class="text-sm font-medium">{{ caller() }}</div>
2
+ <div class="text-sm font-medium">{{ children }}</div>
3
3
  {% if not field.field.required %}<div class="text-white/60 text-xs">Optional</div>{% endif %}
4
4
  </label>
@@ -0,0 +1,8 @@
1
+ {% use_elements %}
2
+
3
+ <div class="space-y-1">
4
+ <admin.Label field={field}>{{ label }}</admin.Label>
5
+ <admin.Select field={field} />
6
+ {% if help is defined %}<admin.Help help={help} />{% endif %}
7
+ <admin.FieldErrors field={field} />
8
+ </div>
@@ -2,5 +2,5 @@
2
2
  type="submit"
3
3
  class="block w-full px-4 py-2 mt-8 text-white bg-blue-600 rounded hover:bg-blue-700"
4
4
  >
5
- {{ caller() }}
5
+ {{ children }}
6
6
  </button>
@@ -1,3 +1,5 @@
1
+ {% use_elements %}
2
+
1
3
  <div class="space-y-1">
2
4
  <admin.Label field=field>{{ label }}</admin.Label>
3
5
  <admin.Textarea field=field />
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain.admin"
3
- version = "0.29.1"
3
+ version = "0.31.0"
4
4
  description = "Admin dashboard and tools for Plain."
5
5
  authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
6
6
  license = "BSD-3-Clause"
@@ -1,141 +0,0 @@
1
- /*
2
- These standard CSS rules are intended to make it easier for the
3
- end-user to drop in HTML elements and have a decent, consistent starting point.
4
-
5
- We shouldn't use many custom classes in here (like .btn) because the user
6
- could unintentionally overwrite those since our CSS is combined.
7
- */
8
- table {
9
- width: 100%;
10
- font-size: .875rem;
11
- line-height: 1.25rem;
12
- table-layout: auto;
13
- }
14
-
15
- th {
16
- text-align: left;
17
- padding: 0.5rem 0.5rem;
18
- }
19
-
20
- table th a {
21
- color: rgba(255, 255, 255, 0.60);
22
- }
23
-
24
- table td a {
25
- color: white;
26
-
27
- &:hover {
28
- text-decoration: none;
29
- }
30
- }
31
-
32
- tbody tr:hover {
33
- background-color: rgb(255, 255, 255, 0.1);
34
- }
35
-
36
- td {
37
- padding: 0.5rem 0.5rem;
38
- white-space: nowrap;
39
- border-bottom: 1px solid rgb(255, 255, 255, 0.1);
40
- }
41
- tr:last-child td {
42
- border-bottom: none;
43
- }
44
-
45
- table img {
46
- height: 1.2rem;
47
- border-radius: 2px;
48
- }
49
-
50
- main a {
51
- color: rgb(37, 99, 235);
52
- }
53
-
54
- main a:hover {
55
- text-decoration: underline;
56
- }
57
-
58
- select {
59
- border-radius: 6px;
60
- background-color: rgba(255, 255, 255, 0.05);
61
- border: 1px solid rgba(255, 255, 255, 0.1);
62
- }
63
-
64
- .actions a, .actions button, main button {
65
- display: inline-block;
66
- padding: 8px 16px;
67
- font-size: 14px;
68
- font-weight: 500;
69
- color: #ffffff;
70
- text-align: center;
71
- text-decoration: none;
72
- background-color: #2a2826;
73
- border: 1px solid #3f3d3b;
74
- border-radius: 6px;
75
- transition: background-color 0.2s, border-color 0.2s, transform 0.2s;
76
- cursor: pointer;
77
- flex-shrink: 0;
78
-
79
- @media (max-width: 640px) {
80
- font-size: 12px;
81
- padding: 6px 12px;
82
- }
83
-
84
- &:hover {
85
- background-color: #2a2928;
86
- border-color: #504e4c;
87
- transform: translateY(-1px);
88
- }
89
-
90
- &:active {
91
- background-color: #141312;
92
- border-color: #3f3d3b;
93
- transform: translateY(0);
94
- }
95
-
96
- &:focus {
97
- outline: 2px solid #e5e7eb;
98
- outline-offset: 2px;
99
- }
100
- }
101
-
102
- main button[type="submit"] {
103
- background-color: #2563eb;
104
- border-color: #2563eb;
105
-
106
- &:hover {
107
- background-color: #1d4ed8;
108
- border-color: #1d4ed8;
109
- }
110
- &:focus {
111
- outline: 2px solid #2563eb;
112
- outline-offset: 2px;
113
- }
114
- }
115
-
116
- /* Cards use these? */
117
- section {
118
- border: rgba(255, 255, 255, 0.1) 1px solid;
119
- border-radius: 0.275rem;
120
- overflow: auto;
121
- background-color: rgba(255, 255, 255, 0.05);
122
- color: rgba(255, 255, 255, 0.8);
123
- display: flex;
124
- flex-direction: column;
125
- }
126
-
127
- section > header {
128
- padding: 0.5rem;
129
- /* background-color: #FFFCF0; */
130
- border-bottom: rgba(255, 255, 255, 0.05) 1px solid;
131
- }
132
-
133
- section > div {
134
- padding: 0.75rem 0.5rem;
135
- flex-grow: 1;
136
- display: flex;
137
- flex-direction: column;
138
- align-items: center;
139
- justify-content: center;
140
- overflow: auto;
141
- }
@@ -1,83 +0,0 @@
1
- jQuery(function($) {
2
- $("[data-toggle]").on("click", function(e) {
3
- e.preventDefault();
4
- var targets = $(this).data("toggle").split(",");
5
- $.each(targets, function(index, target) {
6
- var $target = $(target);
7
- if ($target.data("toggle-class")) {
8
- $target.toggleClass($target.data("toggle-class"));
9
- } else {
10
- $target.toggle();
11
- }
12
- });
13
- });
14
-
15
- $("[data-autosubmit]").on("change", function(e) {
16
- $(this).closest("form").submit();
17
- });
18
-
19
- function createDropdowns(target) {
20
- $(target).find("[data-dropdown]").each(function() {
21
- var template = this.querySelector("template");
22
- tippy(this, {
23
- content: template.innerHTML,
24
- trigger: "click",
25
- allowHTML: true,
26
- interactive: true,
27
- duration: 100,
28
- placement: "bottom-end",
29
- offset: [0, 6],
30
- arrow: false,
31
- appendTo: () => document.body,
32
- onCreate: (instance) => {
33
- instance.popper.classList.add("*:bg-white/15")
34
- instance.popper.classList.add("*:w-48")
35
- instance.popper.classList.add("*:rounded-md")
36
- instance.popper.classList.add("*:shadow-lg")
37
- instance.popper.classList.add("*:ring-1")
38
- instance.popper.classList.add("*:ring-white/20")
39
- },
40
- });
41
- });
42
- }
43
-
44
- function createTooltips(target) {
45
- $(target).find("[data-tooltip]").each(function() {
46
- tippy(this, {
47
- content: this.dataset.tooltip,
48
- duration: 100,
49
- });
50
- });
51
- }
52
-
53
- function autolinkColumns(target) {
54
- $(target).find("[data-column-autolink]").each(function() {
55
- var $this = $(this);
56
- if ($this.find("a").length > 0) {
57
- // Column already has a link, so don't add another
58
- return;
59
- }
60
- var autolinkUrl = $this.data("column-autolink");
61
- if (!autolinkUrl) {
62
- // No URL, so don't add a link
63
- return;
64
- }
65
- var $link = $(document.createElement("a"));
66
- $link.attr("href", autolinkUrl);
67
- $link.addClass("flex p-2 -m-2 text-white/80 hover:no-underline");
68
- $(this).wrapInner($link);
69
- })
70
- }
71
-
72
- createDropdowns(document);
73
- createTooltips(document);
74
- autolinkColumns(document);
75
-
76
- // Search uses htmx to load elements,
77
- // so we need to hook those up too.
78
- htmx.on("htmx:afterSwap", function(evt) {
79
- createDropdowns(evt.detail.target);
80
- createTooltips(evt.detail.target);
81
- autolinkColumns(evt.detail.target);
82
- });
83
- });
@@ -1,8 +0,0 @@
1
- <div>
2
- <div class="flex items-center space-x-2">
3
- <admin.Checkbox field=field />
4
- <admin.Label field=field>{{ label }}</admin.Label>
5
- {% if help is defined %}<admin.Help help=help />{% endif %}
6
- </div>
7
- <admin.FieldErrors field=field />
8
- </div>
@@ -1,6 +0,0 @@
1
- <div class="space-y-1">
2
- <admin.Label field=field>{{ label }}</admin.Label>
3
- <admin.Input field=field />
4
- {% if help is defined %}<admin.Help help=help />{% endif %}
5
- <admin.FieldErrors field=field />
6
- </div>
@@ -1,6 +0,0 @@
1
- <div class="space-y-1">
2
- <admin.Label field=field>{{ label }}</admin.Label>
3
- <admin.Select field=field />
4
- {% if help is defined %}<admin.Help help=help />{% endif %}
5
- <admin.FieldErrors field=field />
6
- </div>
File without changes
File without changes