@geekity/cms 0.36.1 → 0.37.0

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 (194) hide show
  1. package/admin/components/blocks.njk +250 -0
  2. package/admin/components/card.njk +4 -2
  3. package/admin/pages/comments/all.njk +1 -1
  4. package/admin/pages/dashboard/home.njk +33 -32
  5. package/admin/pages/federation/followers.njk +1 -1
  6. package/admin/pages/plugins/screen.njk +5 -35
  7. package/admin/static/admin.css +300 -0
  8. package/dist/admin/blocks.d.ts +133 -0
  9. package/dist/admin/blocks.d.ts.map +1 -0
  10. package/dist/admin/blocks.js +454 -0
  11. package/dist/admin/blocks.js.map +1 -0
  12. package/dist/admin/core-widgets.d.ts +3 -0
  13. package/dist/admin/core-widgets.d.ts.map +1 -0
  14. package/dist/admin/core-widgets.js +17 -0
  15. package/dist/admin/core-widgets.js.map +1 -0
  16. package/dist/admin/engagement.d.ts +16 -0
  17. package/dist/admin/engagement.d.ts.map +1 -0
  18. package/dist/admin/engagement.js +38 -0
  19. package/dist/admin/engagement.js.map +1 -0
  20. package/dist/admin/federation.d.ts +2 -0
  21. package/dist/admin/federation.d.ts.map +1 -1
  22. package/dist/admin/federation.js +2 -1
  23. package/dist/admin/federation.js.map +1 -1
  24. package/dist/admin/formatting.d.ts +1 -0
  25. package/dist/admin/formatting.d.ts.map +1 -1
  26. package/dist/admin/formatting.js +18 -0
  27. package/dist/admin/formatting.js.map +1 -1
  28. package/dist/admin/index.d.ts +1 -1
  29. package/dist/admin/index.d.ts.map +1 -1
  30. package/dist/admin/index.js +1 -1
  31. package/dist/admin/index.js.map +1 -1
  32. package/dist/admin/posts-by-path.d.ts +11 -0
  33. package/dist/admin/posts-by-path.d.ts.map +1 -0
  34. package/dist/admin/posts-by-path.js +59 -0
  35. package/dist/admin/posts-by-path.js.map +1 -0
  36. package/dist/admin/routes.d.ts +0 -2
  37. package/dist/admin/routes.d.ts.map +1 -1
  38. package/dist/admin/routes.js +23 -12
  39. package/dist/admin/routes.js.map +1 -1
  40. package/dist/admin/templates.d.ts.map +1 -1
  41. package/dist/admin/templates.js +2 -0
  42. package/dist/admin/templates.js.map +1 -1
  43. package/dist/admin/tools.js +1 -1
  44. package/dist/admin/tools.js.map +1 -1
  45. package/dist/admin/widget-fediverse.d.ts +3 -0
  46. package/dist/admin/widget-fediverse.d.ts.map +1 -0
  47. package/dist/admin/widget-fediverse.js +49 -0
  48. package/dist/admin/widget-fediverse.js.map +1 -0
  49. package/dist/admin/widget-followers.d.ts +3 -0
  50. package/dist/admin/widget-followers.d.ts.map +1 -0
  51. package/dist/admin/widget-followers.js +37 -0
  52. package/dist/admin/widget-followers.js.map +1 -0
  53. package/dist/admin/widget-health.d.ts +3 -0
  54. package/dist/admin/widget-health.d.ts.map +1 -0
  55. package/dist/admin/widget-health.js +103 -0
  56. package/dist/admin/widget-health.js.map +1 -0
  57. package/dist/admin/widget-interactions.d.ts +4 -0
  58. package/dist/admin/widget-interactions.d.ts.map +1 -0
  59. package/dist/admin/widget-interactions.js +99 -0
  60. package/dist/admin/widget-interactions.js.map +1 -0
  61. package/dist/admin/widget-posting.d.ts +3 -0
  62. package/dist/admin/widget-posting.d.ts.map +1 -0
  63. package/dist/admin/widget-posting.js +111 -0
  64. package/dist/admin/widget-posting.js.map +1 -0
  65. package/dist/admin/widget-scheduled.d.ts +4 -0
  66. package/dist/admin/widget-scheduled.d.ts.map +1 -0
  67. package/dist/admin/widget-scheduled.js +30 -0
  68. package/dist/admin/widget-scheduled.js.map +1 -0
  69. package/dist/admin/widget-top-posts.d.ts +4 -0
  70. package/dist/admin/widget-top-posts.d.ts.map +1 -0
  71. package/dist/admin/widget-top-posts.js +55 -0
  72. package/dist/admin/widget-top-posts.js.map +1 -0
  73. package/dist/admin/widgets.d.ts +35 -0
  74. package/dist/admin/widgets.d.ts.map +1 -0
  75. package/dist/admin/widgets.js +103 -0
  76. package/dist/admin/widgets.js.map +1 -0
  77. package/dist/cms/app.d.ts.map +1 -1
  78. package/dist/cms/app.js +2 -1
  79. package/dist/cms/app.js.map +1 -1
  80. package/dist/cms/boot.d.ts.map +1 -1
  81. package/dist/cms/boot.js +3 -2
  82. package/dist/cms/boot.js.map +1 -1
  83. package/dist/cms/services.d.ts.map +1 -1
  84. package/dist/cms/services.js +19 -0
  85. package/dist/cms/services.js.map +1 -1
  86. package/dist/content/alt-text.d.ts +1 -0
  87. package/dist/content/alt-text.d.ts.map +1 -1
  88. package/dist/content/alt-text.js +26 -1
  89. package/dist/content/alt-text.js.map +1 -1
  90. package/dist/content/sqlite/catalog.d.ts.map +1 -1
  91. package/dist/content/sqlite/catalog.js +15 -2
  92. package/dist/content/sqlite/catalog.js.map +1 -1
  93. package/dist/content/store.d.ts +10 -0
  94. package/dist/content/store.d.ts.map +1 -1
  95. package/dist/content/store.js.map +1 -1
  96. package/dist/content/write.d.ts.map +1 -1
  97. package/dist/content/write.js +3 -1
  98. package/dist/content/write.js.map +1 -1
  99. package/dist/federation/backfill.d.ts +1 -1
  100. package/dist/federation/backfill.d.ts.map +1 -1
  101. package/dist/federation/backfill.js +5 -1
  102. package/dist/federation/backfill.js.map +1 -1
  103. package/dist/federation/follower-history.d.ts +16 -0
  104. package/dist/federation/follower-history.d.ts.map +1 -0
  105. package/dist/federation/follower-history.js +61 -0
  106. package/dist/federation/follower-history.js.map +1 -0
  107. package/dist/federation/inbox.d.ts.map +1 -1
  108. package/dist/federation/inbox.js +2 -1
  109. package/dist/federation/inbox.js.map +1 -1
  110. package/dist/federation/records.d.ts +1 -0
  111. package/dist/federation/records.d.ts.map +1 -1
  112. package/dist/federation/records.js +24 -6
  113. package/dist/federation/records.js.map +1 -1
  114. package/dist/files/data-files.d.ts +1 -0
  115. package/dist/files/data-files.d.ts.map +1 -1
  116. package/dist/files/data-files.js +6 -0
  117. package/dist/files/data-files.js.map +1 -1
  118. package/dist/jobs/queue.d.ts.map +1 -1
  119. package/dist/jobs/queue.js +5 -0
  120. package/dist/jobs/queue.js.map +1 -1
  121. package/dist/jobs/types.d.ts +7 -0
  122. package/dist/jobs/types.d.ts.map +1 -1
  123. package/dist/jobs/types.js.map +1 -1
  124. package/dist/plugin.d.ts +275 -21
  125. package/dist/plugin.d.ts.map +1 -1
  126. package/dist/plugin.js +9 -8
  127. package/dist/plugin.js.map +1 -1
  128. package/dist/plugins/fetch.d.ts +0 -5
  129. package/dist/plugins/fetch.d.ts.map +1 -1
  130. package/dist/plugins/fetch.js +31 -7
  131. package/dist/plugins/fetch.js.map +1 -1
  132. package/dist/plugins/host-api.d.ts +3 -0
  133. package/dist/plugins/host-api.d.ts.map +1 -0
  134. package/dist/plugins/host-api.js +8 -0
  135. package/dist/plugins/host-api.js.map +1 -0
  136. package/dist/plugins/install.d.ts.map +1 -1
  137. package/dist/plugins/install.js +4 -2
  138. package/dist/plugins/install.js.map +1 -1
  139. package/dist/plugins/page-tags.d.ts +8 -0
  140. package/dist/plugins/page-tags.d.ts.map +1 -0
  141. package/dist/plugins/page-tags.js +73 -0
  142. package/dist/plugins/page-tags.js.map +1 -0
  143. package/dist/plugins/registry.d.ts +4 -1
  144. package/dist/plugins/registry.d.ts.map +1 -1
  145. package/dist/plugins/registry.js +50 -9
  146. package/dist/plugins/registry.js.map +1 -1
  147. package/dist/plugins/site.js +1 -1
  148. package/dist/plugins/site.js.map +1 -1
  149. package/dist/store/admin-store.d.ts +2 -2
  150. package/dist/store/admin-store.d.ts.map +1 -1
  151. package/dist/store/admin-store.js.map +1 -1
  152. package/dist/store/comments.d.ts +7 -0
  153. package/dist/store/comments.d.ts.map +1 -1
  154. package/dist/store/comments.js +21 -0
  155. package/dist/store/comments.js.map +1 -1
  156. package/dist/store/deliveries.d.ts +1 -0
  157. package/dist/store/deliveries.d.ts.map +1 -1
  158. package/dist/store/deliveries.js +6 -0
  159. package/dist/store/deliveries.js.map +1 -1
  160. package/dist/store/inbox.d.ts +16 -0
  161. package/dist/store/inbox.d.ts.map +1 -1
  162. package/dist/store/inbox.js +62 -0
  163. package/dist/store/inbox.js.map +1 -1
  164. package/dist/store/queue-database.d.ts +6 -0
  165. package/dist/store/queue-database.d.ts.map +1 -1
  166. package/dist/store/queue-database.js +16 -1
  167. package/dist/store/queue-database.js.map +1 -1
  168. package/dist/store/webmentions.d.ts +1 -0
  169. package/dist/store/webmentions.d.ts.map +1 -1
  170. package/dist/store/webmentions.js +6 -0
  171. package/dist/store/webmentions.js.map +1 -1
  172. package/dist/web/env.d.ts +3 -0
  173. package/dist/web/env.d.ts.map +1 -1
  174. package/dist/web/feed-item.d.ts +4 -3
  175. package/dist/web/feed-item.d.ts.map +1 -1
  176. package/dist/web/feed-item.js +8 -2
  177. package/dist/web/feed-item.js.map +1 -1
  178. package/dist/web/listing-pages.d.ts.map +1 -1
  179. package/dist/web/listing-pages.js +7 -2
  180. package/dist/web/listing-pages.js.map +1 -1
  181. package/dist/web/page-tags.d.ts +4 -0
  182. package/dist/web/page-tags.d.ts.map +1 -0
  183. package/dist/web/page-tags.js +39 -0
  184. package/dist/web/page-tags.js.map +1 -0
  185. package/dist/web/render.d.ts +2 -0
  186. package/dist/web/render.d.ts.map +1 -1
  187. package/dist/web/render.js +14 -0
  188. package/dist/web/render.js.map +1 -1
  189. package/dist/web/routes.d.ts +2 -1
  190. package/dist/web/routes.d.ts.map +1 -1
  191. package/dist/web/routes.js +3 -1
  192. package/dist/web/routes.js.map +1 -1
  193. package/package.json +1 -1
  194. package/themes/default/layouts/base.njk +6 -4
@@ -0,0 +1,250 @@
1
+ {% from "components/table.njk" import table %}
2
+
3
+ {% set SERIES = [
4
+ { dash: 'none', marker: 'circle' },
5
+ { dash: '6 4', marker: 'square' },
6
+ { dash: '2 3', marker: 'triangle' },
7
+ { dash: '10 3 2 3', marker: 'diamond' }
8
+ ] %}
9
+
10
+ {% set TINTS = {
11
+ '0': 'fill-primary stroke-primary',
12
+ '1': 'fill-secondary stroke-secondary',
13
+ '2': 'fill-accent stroke-accent',
14
+ '3': 'fill-info stroke-info'
15
+ } %}
16
+
17
+ {% set TONES = { better: 'text-success', worse: 'text-error', 'none': '' } %}
18
+
19
+ {% macro text(parts) -%}
20
+ {%- for part in parts -%}
21
+ {%- if part.code is defined -%}<code>{{ part.code }}</code>{%- else -%}{{ part }}{%- endif -%}
22
+ {%- endfor -%}
23
+ {%- endmacro %}
24
+
25
+ {% macro marker(shape, x, y) -%}
26
+ {%- if shape == 'circle' -%}
27
+ <circle class="stroke-base-100" stroke-width="2" cx="{{ x }}" cy="{{ y }}" r="4" />
28
+ {%- elif shape == 'square' -%}
29
+ <rect class="stroke-base-100" stroke-width="2" x="{{ x - 4 }}" y="{{ y - 4 }}" width="8" height="8" />
30
+ {%- elif shape == 'triangle' -%}
31
+ <polygon class="stroke-base-100" stroke-width="2" points="{{ x }},{{ y - 5 }} {{ x + 5 }},{{ y + 4 }} {{ x - 5 }},{{ y + 4 }}" />
32
+ {%- else -%}
33
+ <polygon class="stroke-base-100" stroke-width="2" points="{{ x }},{{ y - 5 }} {{ x + 5 }},{{ y }} {{ x }},{{ y + 5 }} {{ x - 5 }},{{ y }}" />
34
+ {%- endif -%}
35
+ {%- endmacro %}
36
+
37
+ {% macro textures(id, count) -%}
38
+ <defs>
39
+ {% if count > 1 %}
40
+ <pattern id="{{ id }}-texture-1" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
41
+ <line class="stroke-base-100" x1="0" y1="0" x2="0" y2="6" stroke-width="2" />
42
+ </pattern>
43
+ {% endif %}
44
+ {% if count > 2 %}
45
+ <pattern id="{{ id }}-texture-2" width="5" height="5" patternUnits="userSpaceOnUse">
46
+ <circle class="fill-base-100" cx="2.5" cy="2.5" r="1.25" />
47
+ </pattern>
48
+ {% endif %}
49
+ {% if count > 3 %}
50
+ <pattern id="{{ id }}-texture-3" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(-45)">
51
+ <line class="stroke-base-100" x1="0" y1="0" x2="0" y2="6" stroke-width="2" />
52
+ </pattern>
53
+ {% endif %}
54
+ </defs>
55
+ {%- endmacro %}
56
+
57
+ {% macro numbers(view) -%}
58
+ {% if view.rows.length %}
59
+ <details class="text-sm">
60
+ <summary class="cursor-pointer text-base-content/70">Show the numbers</summary>
61
+ {% call table(view.description, inCard=true) %}
62
+ <thead>
63
+ <tr>{% for column in view.columns %}<th scope="col">{{ column }}</th>{% endfor %}</tr>
64
+ </thead>
65
+ <tbody>
66
+ {% for row in view.rows %}
67
+ <tr><th scope="row">{{ row.heading }}</th>{% for cell in row.cells %}<td>{{ cell }}</td>{% endfor %}</tr>
68
+ {% endfor %}
69
+ </tbody>
70
+ {% endcall %}
71
+ </details>
72
+ {% endif %}
73
+ {%- endmacro %}
74
+
75
+ {% macro timeSeries(view) -%}
76
+ <div class="grid gap-3">
77
+ <p id="{{ view.id }}" class="sr-only">{{ view.description }}</p>
78
+ {% if view.series.length > 1 %}
79
+ <ul class="flex flex-wrap gap-x-4 gap-y-1 text-sm" aria-hidden="true">
80
+ {% for entry in view.series %}
81
+ {% set look = SERIES[loop.index0] %}
82
+ <li class="flex items-center gap-2">
83
+ <svg class="h-3 w-7" viewBox="0 0 28 12">
84
+ <g class="{{ TINTS | modifier(loop.index0 | string) | trim }}">
85
+ <line x1="0" y1="6" x2="28" y2="6" stroke-width="2" stroke-dasharray="{{ look.dash }}" />
86
+ {{ marker(look.marker, 14, 6) }}
87
+ </g>
88
+ </svg>
89
+ <span>{{ entry.name }}</span></li>
90
+ {% endfor %}
91
+ </ul>
92
+ {% endif %}
93
+ <svg role="img" aria-labelledby="{{ view.id }}" viewBox="0 0 {{ view.width }} {{ view.height }}" class="h-auto w-full max-w-xl">
94
+ {% for tick in view.yTicks %}
95
+ <line class="{{ 'stroke-base-content/30' if loop.first else 'stroke-base-content/10' }}" x1="{{ view.plot.left }}" x2="{{ view.plot.right }}" y1="{{ tick.y }}" y2="{{ tick.y }}" stroke-width="1" />
96
+ <text class="fill-base-content/70" x="{{ view.plot.left - 6 }}" y="{{ tick.y + 4 }}" text-anchor="end" font-size="12">{{ tick.label }}</text>
97
+ {% endfor %}
98
+ {% for tick in view.xTicks %}
99
+ <text class="fill-base-content/70" x="{{ tick.x }}" y="{{ view.height - 8 }}" text-anchor="middle" font-size="12">{{ tick.label }}</text>
100
+ {% endfor %}
101
+ {% for entry in view.series %}
102
+ {% set look = SERIES[loop.index0] %}
103
+ <g class="{{ TINTS | modifier(loop.index0 | string) | trim }}">
104
+ <path class="fill-none" stroke-dasharray="{{ look.dash }}" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" d="{{ entry.path }}" />
105
+ </g>
106
+ {% endfor %}
107
+ {% for entry in view.series %}
108
+ {% set look = SERIES[loop.index0] %}
109
+ <g class="{{ TINTS | modifier(loop.index0 | string) | trim }}">{{ marker(look.marker, entry.end.x, entry.end.y) }}</g>
110
+ {% endfor %}
111
+ {% for band in view.bands %}
112
+ <rect class="fill-transparent" x="{{ band.x }}" y="{{ view.plot.top }}" width="{{ band.width }}" height="{{ view.plot.bottom - view.plot.top }}" pointer-events="all"><title>{{ band.title }}</title></rect>
113
+ {% endfor %}
114
+ </svg>
115
+ {{ numbers(view) }}
116
+ </div>
117
+ {%- endmacro %}
118
+
119
+ {% macro bars(view) -%}
120
+ <div class="grid gap-3">
121
+ <p id="{{ view.id }}" class="sr-only">{{ view.description }}</p>
122
+ {% if view.series.length > 1 %}
123
+ <ul class="flex flex-wrap gap-x-4 gap-y-1 text-sm" aria-hidden="true">
124
+ {% for name in view.series %}
125
+ <li class="flex items-center gap-2">
126
+ <svg class="size-3" viewBox="0 0 12 12">
127
+ <g class="{{ TINTS | modifier(loop.index0 | string) | trim }}"><rect width="12" height="12" rx="2" /></g>
128
+ {% if not loop.first %}<rect width="12" height="12" rx="2" fill="url(#{{ view.id }}-texture-{{ loop.index0 }})" />{% endif %}
129
+ </svg>
130
+ <span>{{ name }}</span></li>
131
+ {% endfor %}
132
+ </ul>
133
+ {% endif %}
134
+ <svg role="img" aria-labelledby="{{ view.id }}" viewBox="0 0 {{ view.width }} {{ view.height }}" class="h-auto w-full max-w-xl">
135
+ {{ textures(view.id, view.series.length) }}
136
+ {% for tick in view.yTicks %}
137
+ <line class="{{ 'stroke-base-content/30' if loop.first else 'stroke-base-content/10' }}" x1="{{ view.plot.left }}" x2="{{ view.plot.right }}" y1="{{ tick.y }}" y2="{{ tick.y }}" stroke-width="1" />
138
+ <text class="fill-base-content/70" x="{{ view.plot.left - 6 }}" y="{{ tick.y + 4 }}" text-anchor="end" font-size="12">{{ tick.label }}</text>
139
+ {% endfor %}
140
+ {% for bar in view.bars %}
141
+ {% for segment in bar.segments %}
142
+ <g class="{{ TINTS | modifier(segment.series | string) | trim }}"><rect x="{{ segment.x }}" y="{{ segment.y }}" width="{{ segment.width }}" height="{{ segment.height }}"><title>{{ segment.title }}</title></rect></g>
143
+ {% if segment.series > 0 %}<rect x="{{ segment.x }}" y="{{ segment.y }}" width="{{ segment.width }}" height="{{ segment.height }}" fill="url(#{{ view.id }}-texture-{{ segment.series }})" pointer-events="none" />{% endif %}
144
+ {% endfor %}
145
+ {% if bar.total %}
146
+ <text class="fill-base-content font-semibold" x="{{ bar.centerX }}" y="{{ bar.total.y }}" text-anchor="middle" font-size="11">{{ bar.total.text }}</text>
147
+ {% endif %}
148
+ {% if bar.tick %}
149
+ <text class="fill-base-content/70" x="{{ bar.centerX }}" y="{{ view.height - 8 }}" text-anchor="middle" font-size="12">{{ bar.tick }}</text>
150
+ {% endif %}
151
+ {% endfor %}
152
+ </svg>
153
+ {{ numbers(view) }}
154
+ </div>
155
+ {%- endmacro %}
156
+
157
+ {% macro calendar(view) -%}
158
+ <div class="grid gap-3">
159
+ <p id="{{ view.id }}" class="sr-only">{{ view.description }}</p>
160
+ <div class="flex flex-row-reverse overflow-x-auto">
161
+ <svg role="img" aria-labelledby="{{ view.id }}" viewBox="0 0 {{ view.width }} {{ view.height }}" width="{{ view.width * 1.25 }}" height="{{ view.height * 1.25 }}" class="mr-auto h-auto{% if view.width * 1.25 > 640 %} max-w-full min-w-160{% endif %} shrink-0">
162
+ {% for month in view.months %}
163
+ <text class="fill-base-content/70" x="{{ month.x }}" y="11" text-anchor="start" font-size="10">{{ month.label }}</text>
164
+ {% endfor %}
165
+ {% for day in view.weekdays %}
166
+ <text class="fill-base-content/70" x="24" y="{{ day.y }}" text-anchor="end" font-size="9">{{ day.label }}</text>
167
+ {% endfor %}
168
+ {% for cell in view.cells %}
169
+ <rect class="{% if cell.level == 0 %}fill-base-content/10{% elif cell.level == 1 %}fill-primary/25{% elif cell.level == 2 %}fill-primary/50{% elif cell.level == 3 %}fill-primary/75{% else %}fill-primary{% endif %}" x="{{ cell.x }}" y="{{ cell.y }}" width="{{ view.cell }}" height="{{ view.cell }}" rx="2"><title>{{ cell.title }}</title></rect>
170
+ {% endfor %}
171
+ </svg>
172
+ </div>
173
+ <div class="flex items-center gap-1 text-xs text-base-content/70" aria-hidden="true">
174
+ <span>Less</span>
175
+ <svg class="h-3 w-[4.25rem]" viewBox="0 0 68 12">
176
+ <rect class="fill-base-content/10" x="0" y="0" width="12" height="12" rx="2" />
177
+ <rect class="fill-primary/25" x="14" y="0" width="12" height="12" rx="2" />
178
+ <rect class="fill-primary/50" x="28" y="0" width="12" height="12" rx="2" />
179
+ <rect class="fill-primary/75" x="42" y="0" width="12" height="12" rx="2" />
180
+ <rect class="fill-primary" x="56" y="0" width="12" height="12" rx="2" />
181
+ </svg>
182
+ <span>More</span>
183
+ </div>
184
+ {{ numbers(view) }}
185
+ </div>
186
+ {%- endmacro %}
187
+
188
+ {% macro block(data) -%}
189
+ {% set view = data | drawn %}
190
+ {% if view.kind == 'paragraph' %}
191
+ <p class="text-sm">{{ text(view.parts) }}</p>
192
+ {% elif view.kind == 'table' %}
193
+ {% call table(view.table.caption, inCard=true) %}
194
+ <thead>
195
+ <tr>
196
+ {% for column in view.table.columns %}<th scope="col">{{ column }}</th>{% endfor %}
197
+ </tr>
198
+ </thead>
199
+ <tbody>
200
+ {% for row in view.table.rows %}
201
+ <tr>
202
+ {% for cell in row %}
203
+ {% if cell.time is defined %}
204
+ <td class="whitespace-nowrap">{% if cell.time %}{{ cell.time | date('readable') }}{% else %}Never{% endif %}</td>
205
+ {% elif cell.text is defined %}
206
+ <td class="wrap-anywhere">{% if cell.href %}<a class="link" href="{{ cell.href }}">{{ cell.text }}</a>{% else %}{{ cell.text }}{% endif %}</td>
207
+ {% else %}
208
+ <td class="wrap-anywhere">{{ text([cell]) }}</td>
209
+ {% endif %}
210
+ {% endfor %}
211
+ </tr>
212
+ {% endfor %}
213
+ </tbody>
214
+ {% endcall %}
215
+ {% elif view.kind == 'stats' %}
216
+ <div class="stats flex flex-wrap gap-px bg-base-content/10 shadow">
217
+ {% for stat in view.stats %}
218
+ <div class="stat w-auto flex-auto border-0 bg-base-100">
219
+ <div class="stat-title">{{ stat.label }}</div>
220
+ <div class="stat-value">{{ stat.value }}</div>
221
+ {% if stat.change %}
222
+ <div class="stat-desc{{ TONES | modifier(stat.change.tone) }}">{% if stat.change.arrow %}<span aria-hidden="true">{{ stat.change.arrow }}</span> {% endif %}{{ stat.change.text }}</div>
223
+ {% endif %}
224
+ </div>
225
+ {% endfor %}
226
+ </div>
227
+ {% elif view.kind == 'timeSeries' %}
228
+ {{ timeSeries(view) }}
229
+ {% elif view.kind == 'ranked' %}
230
+ <ol aria-label="{{ view.caption }}" class="grid gap-3 text-sm">
231
+ {% for item in view.items %}
232
+ <li class="grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 gap-y-1">
233
+ <span class="truncate">{% if item.href %}<a class="link" href="{{ item.href }}">{{ item.label }}</a>{% else %}{{ item.label }}{% endif %}</span>
234
+ <span class="tabular-nums">{{ item.value }}</span>
235
+ <svg class="col-span-2 h-1.5 w-full" viewBox="0 0 100 6" preserveAspectRatio="none" aria-hidden="true">
236
+ <rect class="fill-base-content/10" width="100" height="6" />
237
+ <rect class="fill-primary" width="{{ item.widthPercent }}" height="6" />
238
+ </svg>
239
+ </li>
240
+ {% endfor %}
241
+ </ol>
242
+ {% elif view.kind == 'bars' %}
243
+ {{ bars(view) }}
244
+ {% elif view.kind == 'calendar' %}
245
+ {{ calendar(view) }}
246
+ {% else %}
247
+ {% if view.caption %}<p class="text-sm font-semibold">{{ view.caption }}</p>{% endif %}
248
+ <p class="text-sm text-base-content/70">No data to show.</p>
249
+ {% endif %}
250
+ {%- endmacro %}
@@ -15,6 +15,7 @@
15
15
 
16
16
  size xs, sm, md, lg, xl
17
17
  style border, dash
18
+ tone warning, a warning-coloured border for a card that flags a problem
18
19
 
19
20
  A value outside its set is a render error.
20
21
 
@@ -23,9 +24,10 @@
23
24
 
24
25
  {% set SIZES = { xs: 'card-xs', sm: 'card-sm', md: 'card-md', lg: 'card-lg', xl: 'card-xl' } %}
25
26
  {% set STYLES = { border: 'card-border', dash: 'card-dash' } %}
27
+ {% set TONES = { warning: 'border-2 border-warning' } %}
26
28
 
27
- {% macro card(title='', size=none, style=none) -%}
28
- <div class="card bg-base-100 shadow-sm{{ SIZES | modifier(size) }}{{ STYLES | modifier(style) }}">
29
+ {% macro card(title='', size=none, style=none, tone=none) -%}
30
+ <div class="card bg-base-100 shadow-sm{{ SIZES | modifier(size) }}{{ STYLES | modifier(style) }}{{ TONES | modifier(tone) }}">
29
31
  <div class="card-body">
30
32
  {% if title %}
31
33
  <h2 class="card-title">{{ title }}</h2>
@@ -31,7 +31,7 @@
31
31
  {% else %}
32
32
  <ul class="mb-6 flex max-w-3xl flex-col gap-4">
33
33
  {% for row in rows %}
34
- <li>
34
+ <li id="comment-{{ row.id }}">
35
35
  {% call card() %}
36
36
  <p class="flex flex-wrap items-center gap-x-3 gap-y-1">
37
37
  <span class="font-semibold">{{ row.author }}</span>
@@ -1,11 +1,23 @@
1
1
  {% extends "layouts/shell.njk" %}
2
2
  {% from "components/stat.njk" import stats, stat %}
3
3
  {% from "components/card.njk" import card %}
4
- {% from "components/table.njk" import table %}
5
- {% from "components/badge.njk" import status %}
4
+ {% from "components/badge.njk" import badge %}
5
+ {% from "components/blocks.njk" import block %}
6
+ {% from "components/tabs.njk" import tabs %}
6
7
 
7
8
  {% block title %}Dashboard{% endblock %}
8
9
 
10
+ {% macro widgetCard(widget, tone=none) -%}
11
+ {% call card(title=widget.title, tone=tone) %}
12
+ {% if widget.status == 'stale' %}
13
+ <p class="text-sm">{{ badge('Stale', color='warning') }} The latest load failed. These figures are from {{ widget.loadedAt }}.</p>
14
+ {% elif widget.status == 'unavailable' %}
15
+ <p>These stats are unavailable right now.</p>
16
+ {% endif %}
17
+ {% for data in widget.blocks %}{{ block(data) }}{% endfor %}
18
+ {% endcall %}
19
+ {%- endmacro %}
20
+
9
21
  {% block content %}
10
22
  <h1 class="mb-6 text-2xl font-bold">Dashboard</h1>
11
23
 
@@ -27,34 +39,23 @@
27
39
  </div>
28
40
  </section>
29
41
 
30
- {% call card(title='Recent posts') %}
31
- {% if recent.length %}
32
- {% call table('Recent posts', inCard=true) %}
33
- <thead>
34
- <tr>
35
- <th scope="col">Title</th>
36
- <th scope="col">Date</th>
37
- <th scope="col">Status</th>
38
- </tr>
39
- </thead>
40
- <tbody>
41
- {% for post in recent %}
42
- <tr>
43
- <td><a class="link link-hover" href="{{ post.editUrl }}">{{ post.title }}</a></td>
44
- <td class="whitespace-nowrap">{{ post.date | date('html') }}</td>
45
- <td>
46
- {% if post.draft %}
47
- {{ status('draft', 'Draft') }}
48
- {% else %}
49
- {{ status('published', 'Published') }}
50
- {% endif %}
51
- </td>
52
- </tr>
53
- {% endfor %}
54
- </tbody>
55
- {% endcall %}
56
- {% else %}
57
- <p>Nothing written yet.</p>
58
- {% endif %}
59
- {% endcall %}
42
+ {% for widget in alerts %}
43
+ <section class="mb-8">{{ widgetCard(widget, tone='warning') }}</section>
44
+ {% endfor %}
45
+
46
+ {% if widgets.length %}
47
+ <div class="@container">
48
+ <div class="mb-3 flex flex-wrap items-center justify-between gap-3">
49
+ <h2 class="text-lg font-semibold">Activity</h2>
50
+ {{ tabs(periods, 'Period', size='sm') }}
51
+ </div>
52
+ <div class="grid grid-cols-1 gap-6 @3xl:grid-cols-2">
53
+ {% for widget in widgets %}
54
+ <div class="min-w-0{% if widget.width == 'full' %} @3xl:col-span-2{% endif %}">
55
+ {{ widgetCard(widget) }}
56
+ </div>
57
+ {% endfor %}
58
+ </div>
59
+ </div>
60
+ {% endif %}
60
61
  {% endblock %}
@@ -226,7 +226,7 @@
226
226
  {% endcall %}
227
227
  </section>
228
228
 
229
- <section>
229
+ <section id="delivery">
230
230
  <h2 class="mb-3 text-lg font-semibold">Delivery</h2>
231
231
  {% if posts.length %}
232
232
  {% call table('Delivery') %}
@@ -1,18 +1,13 @@
1
1
  {# /admin/plugins/<package name>: a plugin's own screen (decision-33). The
2
- plugin hands over cards of paragraphs and tables as data and this draws
3
- them, so a plugin writes no markup and everything it says is escaped. #}
2
+ plugin hands over cards of blocks as data (components/blocks.njk) and this
3
+ draws them, so a plugin writes no markup and everything it says is
4
+ escaped. #}
4
5
  {% extends "layouts/shell.njk" %}
5
6
  {% import "components/fields.njk" as field %}
6
7
  {% from "components/setting-fields.njk" import settingFields %}
7
8
  {% from "components/button.njk" import button %}
8
9
  {% from "components/card.njk" import card, cardActions %}
9
- {% from "components/table.njk" import table %}
10
-
11
- {% macro text(parts) -%}
12
- {%- for part in parts -%}
13
- {%- if part.code is defined -%}<code>{{ part.code }}</code>{%- else -%}{{ part }}{%- endif -%}
14
- {%- endfor -%}
15
- {%- endmacro %}
10
+ {% from "components/blocks.njk" import block %}
16
11
 
17
12
  {% block title %}{{ title }}{% endblock %}
18
13
 
@@ -56,32 +51,7 @@
56
51
 
57
52
  {% for entry in cards %}
58
53
  {% call card(title=entry.title) %}
59
- {% for block in entry.blocks %}
60
- {% if block.table %}
61
- {% call table(block.table.caption, inCard=true) %}
62
- <thead>
63
- <tr>
64
- {% for column in block.table.columns %}<th scope="col">{{ column }}</th>{% endfor %}
65
- </tr>
66
- </thead>
67
- <tbody>
68
- {% for row in block.table.rows %}
69
- <tr>
70
- {% for cell in row %}
71
- {% if cell.time is defined %}
72
- <td class="whitespace-nowrap">{% if cell.time %}{{ cell.time | date('readable') }}{% else %}Never{% endif %}</td>
73
- {% else %}
74
- <td>{{ text([cell]) }}</td>
75
- {% endif %}
76
- {% endfor %}
77
- </tr>
78
- {% endfor %}
79
- </tbody>
80
- {% endcall %}
81
- {% else %}
82
- <p class="text-sm">{{ text(block.paragraph) }}</p>
83
- {% endif %}
84
- {% endfor %}
54
+ {% for data in entry.blocks %}{{ block(data) }}{% endfor %}
85
55
  {% endcall %}
86
56
  {% endfor %}
87
57
  </div>