@geekity/cms 0.21.0 → 0.23.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.
- package/README.md +42 -4
- package/admin/components/admin-bar.njk +27 -6
- package/admin/components/alert.njk +28 -0
- package/admin/components/badge.njk +58 -0
- package/admin/components/button.njk +56 -0
- package/admin/components/card.njk +40 -0
- package/admin/components/collapse.njk +36 -0
- package/admin/components/copy.njk +20 -0
- package/admin/components/dropdown.njk +32 -0
- package/admin/components/fields.njk +81 -45
- package/admin/components/flash.njk +12 -5
- package/admin/components/menu.njk +51 -0
- package/admin/components/navbar.njk +23 -0
- package/admin/components/pagination.njk +47 -0
- package/admin/components/public-admin-bar.njk +4 -41
- package/admin/components/stat.njk +35 -0
- package/admin/components/table.njk +74 -0
- package/admin/components/tabs.njk +33 -0
- package/admin/components/theme-choice.njk +18 -0
- package/admin/layouts/base.njk +9 -16
- package/admin/layouts/settings-page.njk +15 -10
- package/admin/layouts/shell.njk +23 -31
- package/admin/pages/account/forgot.njk +39 -33
- package/admin/pages/account/login.njk +22 -16
- package/admin/pages/account/reset.njk +34 -25
- package/admin/pages/account/setup.njk +21 -15
- package/admin/pages/appearance/themes.njk +50 -44
- package/admin/pages/comments/all.njk +88 -106
- package/admin/pages/dashboard/home.njk +50 -62
- package/admin/pages/documents/conflict.njk +29 -24
- package/admin/pages/documents/editor.njk +173 -275
- package/admin/pages/documents/list.njk +60 -69
- package/admin/pages/documents/syndication.njk +98 -60
- package/admin/pages/documents/taxonomy.njk +36 -32
- package/admin/pages/error.njk +8 -5
- package/admin/pages/federation/followers.njk +91 -97
- package/admin/pages/federation/settings.njk +10 -11
- package/admin/pages/indieauth/consent.njk +60 -44
- package/admin/pages/indieauth/refused.njk +6 -3
- package/admin/pages/media/library.njk +86 -107
- package/admin/pages/messages/all.njk +71 -77
- package/admin/pages/navigation/menus.njk +90 -85
- package/admin/pages/placeholder.njk +13 -8
- package/admin/pages/settings/discussion.njk +30 -29
- package/admin/pages/settings/email.njk +26 -28
- package/admin/pages/settings/general.njk +10 -4
- package/admin/pages/settings/permalinks.njk +8 -9
- package/admin/pages/settings/privacy.njk +3 -3
- package/admin/pages/settings/reading.njk +3 -3
- package/admin/pages/tools/content-index.njk +79 -73
- package/admin/pages/tools/personal-data.njk +61 -55
- package/admin/pages/users/activity-entry.njk +12 -12
- package/admin/pages/users/activity.njk +13 -19
- package/admin/pages/users/apps.njk +112 -100
- package/admin/pages/users/edit.njk +65 -58
- package/admin/pages/users/list.njk +26 -24
- package/admin/pages/users/new.njk +11 -7
- package/admin/src/admin.css +174 -0
- package/admin/static/admin-bar.css +33 -31
- package/admin/static/admin-bar.js +22 -0
- package/admin/static/admin.css +7388 -1195
- package/admin/static/copy.js +1 -1
- package/admin/static/editor.js +18 -18
- package/dist/admin/accounts.d.ts +20 -0
- package/dist/admin/accounts.d.ts.map +1 -1
- package/dist/admin/accounts.js +25 -0
- package/dist/admin/accounts.js.map +1 -1
- package/dist/admin/admin-theme.d.ts +157 -0
- package/dist/admin/admin-theme.d.ts.map +1 -0
- package/dist/admin/admin-theme.js +52 -0
- package/dist/admin/admin-theme.js.map +1 -0
- package/dist/admin/assets.d.ts +4 -3
- package/dist/admin/assets.d.ts.map +1 -1
- package/dist/admin/assets.js +4 -3
- package/dist/admin/assets.js.map +1 -1
- package/dist/admin/comments.js +2 -2
- package/dist/admin/comments.js.map +1 -1
- package/dist/admin/documents.d.ts +36 -7
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +141 -30
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/editor-layout.d.ts +1 -1
- package/dist/admin/editor-layout.d.ts.map +1 -1
- package/dist/admin/editor-layout.js +6 -1
- package/dist/admin/editor-layout.js.map +1 -1
- package/dist/admin/event-field.d.ts +26 -0
- package/dist/admin/event-field.d.ts.map +1 -0
- package/dist/admin/event-field.js +74 -0
- package/dist/admin/event-field.js.map +1 -0
- package/dist/admin/federation.js +3 -3
- package/dist/admin/federation.js.map +1 -1
- package/dist/admin/headers.js +1 -1
- package/dist/admin/headers.js.map +1 -1
- package/dist/admin/media.js +2 -2
- package/dist/admin/media.js.map +1 -1
- package/dist/admin/preview.d.ts.map +1 -1
- package/dist/admin/preview.js +16 -1
- package/dist/admin/preview.js.map +1 -1
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +17 -8
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/store.d.ts +9 -0
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +16 -5
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/templates.d.ts +6 -3
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +25 -3
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/users.d.ts +4 -0
- package/dist/admin/users.d.ts.map +1 -1
- package/dist/admin/users.js +37 -1
- package/dist/admin/users.js.map +1 -1
- package/dist/avatars/avatars.js +1 -1
- package/dist/avatars/avatars.js.map +1 -1
- package/dist/comments/policy.d.ts +10 -0
- package/dist/comments/policy.d.ts.map +1 -1
- package/dist/comments/policy.js +12 -0
- package/dist/comments/policy.js.map +1 -1
- package/dist/comments/records.d.ts +5 -1
- package/dist/comments/records.d.ts.map +1 -1
- package/dist/comments/records.js +16 -2
- package/dist/comments/records.js.map +1 -1
- package/dist/content/event.d.ts +40 -0
- package/dist/content/event.d.ts.map +1 -0
- package/dist/content/event.js +51 -0
- package/dist/content/event.js.map +1 -0
- package/dist/content/handles.d.ts +22 -0
- package/dist/content/handles.d.ts.map +1 -0
- package/dist/content/handles.js +68 -0
- package/dist/content/handles.js.map +1 -0
- package/dist/content/markdown.d.ts +8 -2
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +55 -5
- package/dist/content/markdown.js.map +1 -1
- package/dist/content/parser.d.ts +3 -0
- package/dist/content/parser.d.ts.map +1 -1
- package/dist/content/parser.js +1 -1
- package/dist/content/parser.js.map +1 -1
- package/dist/content/post-type.d.ts +12 -8
- package/dist/content/post-type.d.ts.map +1 -1
- package/dist/content/post-type.js +19 -4
- package/dist/content/post-type.js.map +1 -1
- package/dist/content/rsvp.d.ts +29 -0
- package/dist/content/rsvp.d.ts.map +1 -0
- package/dist/content/rsvp.js +49 -0
- package/dist/content/rsvp.js.map +1 -0
- package/dist/content/save.d.ts +2 -1
- package/dist/content/save.d.ts.map +1 -1
- package/dist/content/save.js +10 -3
- package/dist/content/save.js.map +1 -1
- package/dist/content/store.d.ts +11 -4
- package/dist/content/store.d.ts.map +1 -1
- package/dist/content/store.js +15 -4
- package/dist/content/store.js.map +1 -1
- package/dist/content/sync.d.ts.map +1 -1
- package/dist/content/sync.js +7 -6
- package/dist/content/sync.js.map +1 -1
- package/dist/env.d.ts +3 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/article.d.ts +9 -3
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +90 -34
- package/dist/federation/article.js.map +1 -1
- package/dist/federation/cited-post.d.ts +13 -0
- package/dist/federation/cited-post.d.ts.map +1 -0
- package/dist/federation/cited-post.js +75 -0
- package/dist/federation/cited-post.js.map +1 -0
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +31 -2
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/handles.d.ts +28 -0
- package/dist/federation/handles.d.ts.map +1 -0
- package/dist/federation/handles.js +65 -0
- package/dist/federation/handles.js.map +1 -0
- package/dist/federation/inbox.d.ts +2 -1
- package/dist/federation/inbox.d.ts.map +1 -1
- package/dist/federation/inbox.js +11 -3
- package/dist/federation/inbox.js.map +1 -1
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +18 -2
- package/dist/federation/mount.js.map +1 -1
- package/dist/federation/profiles.d.ts.map +1 -1
- package/dist/federation/profiles.js +2 -8
- package/dist/federation/profiles.js.map +1 -1
- package/dist/federation/site-loaders.d.ts +15 -0
- package/dist/federation/site-loaders.d.ts.map +1 -0
- package/dist/federation/site-loaders.js +19 -0
- package/dist/federation/site-loaders.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -1
- package/dist/indieauth/client.js +2 -2
- package/dist/indieauth/client.js.map +1 -1
- package/dist/micropub/create.d.ts +2 -1
- package/dist/micropub/create.d.ts.map +1 -1
- package/dist/micropub/create.js +57 -8
- package/dist/micropub/create.js.map +1 -1
- package/dist/micropub/endpoint.d.ts.map +1 -1
- package/dist/micropub/endpoint.js +12 -4
- package/dist/micropub/endpoint.js.map +1 -1
- package/dist/micropub/update.d.ts +3 -0
- package/dist/micropub/update.d.ts.map +1 -1
- package/dist/micropub/update.js +29 -4
- package/dist/micropub/update.js.map +1 -1
- package/dist/web/admin-bar.d.ts +4 -4
- package/dist/web/admin-bar.d.ts.map +1 -1
- package/dist/web/admin-bar.js +11 -34
- package/dist/web/admin-bar.js.map +1 -1
- package/dist/web/context.d.ts +32 -4
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +28 -1
- package/dist/web/context.js.map +1 -1
- package/dist/web/conversation.d.ts +26 -1
- package/dist/web/conversation.d.ts.map +1 -1
- package/dist/web/conversation.js +67 -10
- package/dist/web/conversation.js.map +1 -1
- package/dist/web/documents.d.ts +12 -0
- package/dist/web/documents.d.ts.map +1 -1
- package/dist/web/documents.js +20 -0
- package/dist/web/documents.js.map +1 -1
- package/dist/web/feed-item.d.ts.map +1 -1
- package/dist/web/feed-item.js +8 -3
- package/dist/web/feed-item.js.map +1 -1
- package/dist/web/render.d.ts +3 -0
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +39 -6
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +16 -3
- package/dist/web/routes.js.map +1 -1
- package/dist/web/templates.d.ts +4 -1
- package/dist/web/templates.d.ts.map +1 -1
- package/dist/web/templates.js +11 -0
- package/dist/web/templates.js.map +1 -1
- package/dist/webmention/cited-picture.d.ts +11 -4
- package/dist/webmention/cited-picture.d.ts.map +1 -1
- package/dist/webmention/cited-picture.js +22 -13
- package/dist/webmention/cited-picture.js.map +1 -1
- package/dist/webmention/cited-start.d.ts +10 -0
- package/dist/webmention/cited-start.d.ts.map +1 -0
- package/dist/webmention/cited-start.js +21 -0
- package/dist/webmention/cited-start.js.map +1 -0
- package/dist/webmention/fetch-public.d.ts +2 -2
- package/dist/webmention/fetch-public.d.ts.map +1 -1
- package/dist/webmention/fetch-public.js +1 -1
- package/dist/webmention/fetch-public.js.map +1 -1
- package/dist/webmention/html.d.ts +2 -0
- package/dist/webmention/html.d.ts.map +1 -1
- package/dist/webmention/html.js +6 -0
- package/dist/webmention/html.js.map +1 -1
- package/dist/webmention/index.d.ts +2 -1
- package/dist/webmention/index.d.ts.map +1 -1
- package/dist/webmention/index.js +1 -0
- package/dist/webmention/index.js.map +1 -1
- package/dist/webmention/microformats.d.ts +25 -2
- package/dist/webmention/microformats.d.ts.map +1 -1
- package/dist/webmention/microformats.js +38 -4
- package/dist/webmention/microformats.js.map +1 -1
- package/dist/webmention/pingback.d.ts +42 -0
- package/dist/webmention/pingback.d.ts.map +1 -0
- package/dist/webmention/pingback.js +229 -0
- package/dist/webmention/pingback.js.map +1 -0
- package/dist/webmention/receive.d.ts +20 -2
- package/dist/webmention/receive.d.ts.map +1 -1
- package/dist/webmention/receive.js +25 -16
- package/dist/webmention/receive.js.map +1 -1
- package/dist/webmention/reply-context.d.ts +65 -18
- package/dist/webmention/reply-context.d.ts.map +1 -1
- package/dist/webmention/reply-context.js +403 -99
- package/dist/webmention/reply-context.js.map +1 -1
- package/dist/webmention/reply-contexts.d.ts +3 -1
- package/dist/webmention/reply-contexts.d.ts.map +1 -1
- package/dist/webmention/reply-contexts.js +53 -17
- package/dist/webmention/reply-contexts.js.map +1 -1
- package/dist/webmention/routes.d.ts.map +1 -1
- package/dist/webmention/routes.js +2 -0
- package/dist/webmention/routes.js.map +1 -1
- package/dist/webmention/service.d.ts +11 -0
- package/dist/webmention/service.d.ts.map +1 -1
- package/dist/webmention/service.js +27 -6
- package/dist/webmention/service.js.map +1 -1
- package/dist/webmention/syndication.d.ts +6 -0
- package/dist/webmention/syndication.d.ts.map +1 -1
- package/dist/webmention/syndication.js +12 -0
- package/dist/webmention/syndication.js.map +1 -1
- package/package.json +10 -6
- package/themes/default/README.md +130 -38
- package/themes/default/layouts/410.njk +10 -0
- package/themes/default/layouts/base.njk +3 -0
- package/themes/default/layouts/page.njk +7 -0
- package/themes/default/layouts/post.njk +7 -4
- package/themes/default/layouts/search.njk +1 -1
- package/themes/default/partials/cited-page.njk +7 -0
- package/themes/default/partials/conversation.njk +25 -5
- package/themes/default/partials/event.njk +27 -0
- package/themes/default/partials/jsonld.njk +28 -0
- package/themes/default/partials/post-list.njk +5 -2
- package/themes/default/partials/reply-context.njk +24 -4
- package/themes/default/partials/rsvp.njk +7 -0
- package/themes/default/src/style.css +20 -0
- package/themes/default/static/style.css +35 -0
package/README.md
CHANGED
|
@@ -1154,10 +1154,10 @@ The open web's version of what ActivityPub does: one page telling another that
|
|
|
1154
1154
|
it linked to it. Both directions are on by default and each has a switch on the
|
|
1155
1155
|
Discussion settings page.
|
|
1156
1156
|
|
|
1157
|
-
| Setting in `site.json` | What it does
|
|
1158
|
-
| ---------------------- |
|
|
1159
|
-
| `webmentionsSend` | Tell the external pages a post links to, when it is published or edited.
|
|
1160
|
-
| `webmentionsReceive` | Advertise the
|
|
1157
|
+
| Setting in `site.json` | What it does |
|
|
1158
|
+
| ---------------------- | -------------------------------------------------------------------------- |
|
|
1159
|
+
| `webmentionsSend` | Tell the external pages a post links to, when it is published or edited. |
|
|
1160
|
+
| `webmentionsReceive` | Advertise the endpoints and accept webmentions and pingbacks sent to them. |
|
|
1161
1161
|
|
|
1162
1162
|
**Sending** runs off the same index changes federation does, so a post saved in
|
|
1163
1163
|
the editor and one edited on disk are one thing, and a full scan sends nothing.
|
|
@@ -1187,6 +1187,21 @@ answers 404 or 410 — takes it away. Closing rules do not apply: a post that
|
|
|
1187
1187
|
stopped taking comments still hears about a page that links to it, exactly as it
|
|
1188
1188
|
still hears a fediverse reply.
|
|
1189
1189
|
|
|
1190
|
+
**Pingbacks** are how most WordPress sites say they linked here. The same
|
|
1191
|
+
switch takes them at `/_geekity/pingback`, advertised as an absolute URL in an
|
|
1192
|
+
`X-Pingback` header and a `<link rel="pingback">` on every post and on every
|
|
1193
|
+
page that takes comments. A `pingback.ping(source, target)` XML-RPC call goes
|
|
1194
|
+
through the same check, verification, spam checker and moderation queue as a
|
|
1195
|
+
webmention and is filed as the same comment, so a link pinged and a link
|
|
1196
|
+
mentioned are one entry. Because XML-RPC wants an answer, the source is read
|
|
1197
|
+
before the endpoint replies, and a failure is one of the spec's fault codes:
|
|
1198
|
+
`0x0010` for a source that does not exist, `0x0011` for one that does not link
|
|
1199
|
+
here, `0x0020` for a target that does not exist, `0x0021` for a target that
|
|
1200
|
+
takes no pingbacks, and `0x0030` for a ping sent again, which still rewrites
|
|
1201
|
+
the entry from the source as it now reads. A body over 16 KB, XML that is not
|
|
1202
|
+
well formed, and any document type declaration are refused with `-32700`
|
|
1203
|
+
before anything is resolved. The CMS does not send pingbacks.
|
|
1204
|
+
|
|
1190
1205
|
### Syndication targets
|
|
1191
1206
|
|
|
1192
1207
|
Some services copy a post when it links to them and sends them a webmention,
|
|
@@ -1880,6 +1895,29 @@ flash messages. A message queued with `flash(c, 'notice', '…')` is kept on the
|
|
|
1880
1895
|
session row, shown on the next page the browser asks for, and cleared as it is
|
|
1881
1896
|
read, so it survives exactly one redirect.
|
|
1882
1897
|
|
|
1898
|
+
### The admin's look
|
|
1899
|
+
|
|
1900
|
+
The admin is drawn in DaisyUI 5 on Tailwind v4 (decision-30). Its stylesheet,
|
|
1901
|
+
`admin/static/admin.css`, is compiled at build time from `admin/src/admin.css`
|
|
1902
|
+
and ships in the package like the default theme's. It carries every built-in
|
|
1903
|
+
DaisyUI theme, with `light` as the default and `dark` when the system prefers
|
|
1904
|
+
dark. A site cannot override it or add a theme of its own.
|
|
1905
|
+
|
|
1906
|
+
The admin bar is the same component on the admin and across the top of every
|
|
1907
|
+
public page a signed-in user reads: a `<geekity-admin-bar>` element holding a
|
|
1908
|
+
declarative shadow root, so neither the site theme nor the admin's theme
|
|
1909
|
+
reaches inside it. It draws in a light or a dark palette of its own, following
|
|
1910
|
+
the user's admin theme. On a public page it pushes the page down by
|
|
1911
|
+
`--geekity-admin-bar-height` and adds the `geekity-admin-bar` class to
|
|
1912
|
+
`<html>` for a theme that needs to make room of its own.
|
|
1913
|
+
|
|
1914
|
+
Each user picks the theme the admin is drawn in for themselves, on their own
|
|
1915
|
+
screen under Users: any built-in DaisyUI theme, or Follow the system. The
|
|
1916
|
+
choice is stored as `adminTheme` on the user in `data/users.json`, and every
|
|
1917
|
+
admin page drawn for that user carries it as `data-theme` on `<html>`. Following
|
|
1918
|
+
the system stores nothing and puts no `data-theme` on the page. The login,
|
|
1919
|
+
setup, forgot-password and reset screens never carry one.
|
|
1920
|
+
|
|
1883
1921
|
### The menu
|
|
1884
1922
|
|
|
1885
1923
|
The menu is WordPress classic: twelve sections — Dashboard, Posts, Pages,
|
|
@@ -1,13 +1,33 @@
|
|
|
1
|
-
{# The admin bar, in
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
menu ends it.
|
|
1
|
+
{# The admin bar (decision-30), on every signed-in admin screen and every
|
|
2
|
+
public page a signed-in user reads. The context it takes:
|
|
3
|
+
- `siteHref` and `barLinks`: the admin's title leads to the dashboard, the
|
|
4
|
+
public site's home (TASK-257).
|
|
5
|
+
- `me`, `logoutUrl` and `csrfToken`, for the account menu that ends it.
|
|
6
|
+
- `barScheme`, `adminColorScheme` of the user's admin theme; `auto` puts no
|
|
7
|
+
data-scheme on the host.
|
|
8
|
+
- `cspNonce`, on the admin only.
|
|
9
|
+
- `assetPrefix`, where static/admin-bar.js is served from.
|
|
6
10
|
|
|
7
11
|
WordPress's "Howdy," menu, greeting the way this site's owner does. A
|
|
8
12
|
popover rather than a script: the browser opens and closes it, shuts it on
|
|
9
13
|
Escape or a click elsewhere, and tells a screen reader whether it is
|
|
10
|
-
expanded
|
|
14
|
+
expanded.
|
|
15
|
+
|
|
16
|
+
The offset stylesheet makes room for the fixed bar, and the bar's script
|
|
17
|
+
replaces the height with the one it measures. The margin is on <html>, as
|
|
18
|
+
under WordPress's bar, so a theme's own padding is left alone; `!important`
|
|
19
|
+
outlasts a reset such as `html, body { margin: 0 }`, and `:where()` lets a
|
|
20
|
+
theme with a sticky header of its own keep its scroll padding. #}
|
|
21
|
+
{% set HOST_STYLE = "all: initial; display: block; position: fixed; top: 0; left: 0; right: 0; z-index: 99999" -%}
|
|
22
|
+
<style id="geekity-admin-bar-offset"{% if cspNonce %} nonce="{{ cspNonce }}"{% endif %}>
|
|
23
|
+
:root { --geekity-admin-bar-height: 40px; }
|
|
24
|
+
@media (max-width: 600px) { :root { --geekity-admin-bar-height: 80px; } }
|
|
25
|
+
html { margin-top: var(--geekity-admin-bar-height) !important; }
|
|
26
|
+
:where(html) { scroll-padding-top: var(--geekity-admin-bar-height); }
|
|
27
|
+
</style><geekity-admin-bar
|
|
28
|
+
{%- if barScheme == 'light' or barScheme == 'dark' %} data-scheme="{{ barScheme }}"{% endif %}
|
|
29
|
+
{%- if not cspNonce %} style="{{ HOST_STYLE }}"{% endif %}><template shadowrootmode="open"><style{% if cspNonce %} nonce="{{ cspNonce }}"{% endif %}>:host { {{ HOST_STYLE }}; }
|
|
30
|
+
{% include "static/admin-bar.css" %}</style>
|
|
11
31
|
<nav class="admin-bar" aria-label="Admin bar">
|
|
12
32
|
<a class="admin-bar-site" href="{{ siteHref }}">{{ site.title }}</a>
|
|
13
33
|
{% for link in barLinks %}
|
|
@@ -25,3 +45,4 @@
|
|
|
25
45
|
</div>
|
|
26
46
|
{% endif %}
|
|
27
47
|
</nav>
|
|
48
|
+
<script src="{{ assetPrefix }}admin-bar.js" defer></script></template></geekity-admin-bar>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{# An alert: `{% call alert(…) %}body{% endcall %}` emits
|
|
2
|
+
`<div role="alert" class="alert">body</div>`. The body is markup.
|
|
3
|
+
|
|
4
|
+
Modifiers, each a name from a closed set that maps to DaisyUI classes:
|
|
5
|
+
|
|
6
|
+
color info, success, warning, error
|
|
7
|
+
style outline, dash, soft
|
|
8
|
+
direction vertical, horizontal, or responsive (stacked on a phone,
|
|
9
|
+
in a row from `sm` up)
|
|
10
|
+
|
|
11
|
+
A value outside its set is a render error.
|
|
12
|
+
|
|
13
|
+
`labelledby` names the id of the heading inside the body. `focus` adds
|
|
14
|
+
`tabindex="-1"` and `autofocus`; only one element on a page may ask for it.
|
|
15
|
+
`polite` makes it `role="status"`, read out when the reader is next idle
|
|
16
|
+
rather than at once. #}
|
|
17
|
+
|
|
18
|
+
{% set COLORS = { info: 'alert-info', success: 'alert-success', warning: 'alert-warning', error: 'alert-error' } %}
|
|
19
|
+
{% set STYLES = { outline: 'alert-outline', dash: 'alert-dash', soft: 'alert-soft' } %}
|
|
20
|
+
{% set DIRECTIONS = {
|
|
21
|
+
vertical: 'alert-vertical',
|
|
22
|
+
horizontal: 'alert-horizontal',
|
|
23
|
+
responsive: 'alert-vertical sm:alert-horizontal'
|
|
24
|
+
} %}
|
|
25
|
+
|
|
26
|
+
{% macro alert(color=none, style=none, direction=none, labelledby='', focus=false, polite=false) -%}
|
|
27
|
+
<div role="{{ 'status' if polite else 'alert' }}"{% if labelledby %} aria-labelledby="{{ labelledby }}"{% endif %}{% if focus %} tabindex="-1" autofocus{% endif %} class="alert{{ COLORS | modifier(color) }}{{ STYLES | modifier(style) }}{{ DIRECTIONS | modifier(direction) }}">{{ caller() }}</div>
|
|
28
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{# A badge: `badge(label, …)` emits
|
|
2
|
+
`<span class="badge badge-outline badge-sm">label</span>`, outlined when it
|
|
3
|
+
is given no colour and no style. The label is escaped.
|
|
4
|
+
|
|
5
|
+
Modifiers, each a name from a closed set that maps to one DaisyUI class:
|
|
6
|
+
|
|
7
|
+
color neutral, primary, secondary, accent, info, success, warning, error
|
|
8
|
+
style outline, dash, soft, ghost
|
|
9
|
+
size xs, sm (the default), md, lg, xl
|
|
10
|
+
|
|
11
|
+
A value outside its set is a render error.
|
|
12
|
+
|
|
13
|
+
`status(name, label)` is the badge for the state a row is in, coloured by
|
|
14
|
+
`STATUSES` below; the label is escaped. A name outside that set is a
|
|
15
|
+
render error. #}
|
|
16
|
+
|
|
17
|
+
{% set COLORS = {
|
|
18
|
+
neutral: 'badge-neutral',
|
|
19
|
+
primary: 'badge-primary',
|
|
20
|
+
secondary: 'badge-secondary',
|
|
21
|
+
accent: 'badge-accent',
|
|
22
|
+
info: 'badge-info',
|
|
23
|
+
success: 'badge-success',
|
|
24
|
+
warning: 'badge-warning',
|
|
25
|
+
error: 'badge-error'
|
|
26
|
+
} %}
|
|
27
|
+
{% set STYLES = { outline: 'badge-outline', dash: 'badge-dash', soft: 'badge-soft', ghost: 'badge-ghost' } %}
|
|
28
|
+
{% set SIZES = { xs: 'badge-xs', sm: 'badge-sm', md: 'badge-md', lg: 'badge-lg', xl: 'badge-xl' } %}
|
|
29
|
+
|
|
30
|
+
{% macro badge(label, color=none, style=none, size='sm') -%}
|
|
31
|
+
<span class="badge{% if not color and not style %} badge-outline{% endif %}{{ COLORS | modifier(color) }}{{ STYLES | modifier(style) }}{{ SIZES | modifier(size) }}">{{ label }}</span>
|
|
32
|
+
{%- endmacro %}
|
|
33
|
+
|
|
34
|
+
{% set STATUSES = {
|
|
35
|
+
published: 'badge-outline',
|
|
36
|
+
approved: 'badge-outline',
|
|
37
|
+
accepted: 'badge-outline',
|
|
38
|
+
described: 'badge-outline',
|
|
39
|
+
decorative: 'badge-outline',
|
|
40
|
+
active: 'badge-primary',
|
|
41
|
+
unread: 'badge-primary',
|
|
42
|
+
connected: 'badge-success',
|
|
43
|
+
scheduled: 'badge-info',
|
|
44
|
+
draft: 'badge-warning',
|
|
45
|
+
pending: 'badge-warning',
|
|
46
|
+
missing: 'badge-warning',
|
|
47
|
+
unreachable: 'badge-warning',
|
|
48
|
+
failed: 'badge-error',
|
|
49
|
+
rejected: 'badge-error',
|
|
50
|
+
spam: 'badge-error',
|
|
51
|
+
hidden: 'badge-ghost',
|
|
52
|
+
trashed: 'badge-ghost',
|
|
53
|
+
disconnected: 'badge-ghost'
|
|
54
|
+
} %}
|
|
55
|
+
|
|
56
|
+
{% macro status(name, label) -%}
|
|
57
|
+
<span class="badge badge-sm{{ STATUSES | modifier(name) }}">{{ label }}</span>
|
|
58
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{# A button, and a link drawn as one.
|
|
2
|
+
|
|
3
|
+
`button(label, …)` emits `<button type="button" class="btn btn-sm">`, and
|
|
4
|
+
`buttonLink(label, href, …)` emits `<a class="btn btn-sm" href>`.
|
|
5
|
+
|
|
6
|
+
Each takes the same modifiers:
|
|
7
|
+
|
|
8
|
+
color neutral, primary, secondary, accent, info, success, warning, error
|
|
9
|
+
style outline, dash, soft, ghost, link
|
|
10
|
+
size xs, sm (the default), md, lg, xl
|
|
11
|
+
shape wide, block, square, circle
|
|
12
|
+
active true draws it pressed
|
|
13
|
+
|
|
14
|
+
A value outside its set is a render error.
|
|
15
|
+
`button` also takes `type` (button, unless it submits), `id`, `name`,
|
|
16
|
+
`value`, `formaction`, `formtarget`, `formnovalidate`, `popovertarget`,
|
|
17
|
+
`disabled` and `hidden`. The label is escaped. #}
|
|
18
|
+
|
|
19
|
+
{% set COLORS = {
|
|
20
|
+
neutral: 'btn-neutral',
|
|
21
|
+
primary: 'btn-primary',
|
|
22
|
+
secondary: 'btn-secondary',
|
|
23
|
+
accent: 'btn-accent',
|
|
24
|
+
info: 'btn-info',
|
|
25
|
+
success: 'btn-success',
|
|
26
|
+
warning: 'btn-warning',
|
|
27
|
+
error: 'btn-error'
|
|
28
|
+
} %}
|
|
29
|
+
{% set STYLES = { outline: 'btn-outline', dash: 'btn-dash', soft: 'btn-soft', ghost: 'btn-ghost', link: 'btn-link' } %}
|
|
30
|
+
{% set SIZES = { xs: 'btn-xs', sm: 'btn-sm', md: 'btn-md', lg: 'btn-lg', xl: 'btn-xl' } %}
|
|
31
|
+
{% set SHAPES = { wide: 'btn-wide', block: 'btn-block', square: 'btn-square', circle: 'btn-circle' } %}
|
|
32
|
+
|
|
33
|
+
{% macro button(
|
|
34
|
+
label,
|
|
35
|
+
type='button',
|
|
36
|
+
color=none,
|
|
37
|
+
style=none,
|
|
38
|
+
size='sm',
|
|
39
|
+
shape=none,
|
|
40
|
+
active=false,
|
|
41
|
+
id='',
|
|
42
|
+
name='',
|
|
43
|
+
value='',
|
|
44
|
+
formaction='',
|
|
45
|
+
formtarget='',
|
|
46
|
+
formnovalidate=false,
|
|
47
|
+
popovertarget='',
|
|
48
|
+
disabled=false,
|
|
49
|
+
hidden=false
|
|
50
|
+
) -%}
|
|
51
|
+
<button type="{{ type }}"{% if id %} id="{{ id }}"{% endif %} class="btn{{ COLORS | modifier(color) }}{{ STYLES | modifier(style) }}{{ SIZES | modifier(size) }}{{ SHAPES | modifier(shape) }}{% if active %} btn-active {%- endif %}"{% if name %} name="{{ name }}"{% endif %}{% if value %} value="{{ value }}"{% endif %}{% if formaction %} formaction="{{ formaction }}"{% endif %}{% if formtarget %} formtarget="{{ formtarget }}"{% endif %}{% if formnovalidate %} formnovalidate{% endif %}{% if popovertarget %} popovertarget="{{ popovertarget }}"{% endif %}{% if disabled %} disabled{% endif %}{% if hidden %} hidden{% endif %}>{{ label }}</button>
|
|
52
|
+
{%- endmacro %}
|
|
53
|
+
|
|
54
|
+
{% macro buttonLink(label, href, color=none, style=none, size='sm', shape=none, active=false) -%}
|
|
55
|
+
<a class="btn{{ COLORS | modifier(color) }}{{ STYLES | modifier(style) }}{{ SIZES | modifier(size) }}{{ SHAPES | modifier(shape) }}{% if active %} btn-active {%- endif %}" href="{{ href }}">{{ label }}</a>
|
|
56
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{# A card.
|
|
2
|
+
|
|
3
|
+
`{% call card(title=…) %}body{% endcall %}` emits
|
|
4
|
+
|
|
5
|
+
<div class="card bg-base-100 shadow-sm">
|
|
6
|
+
<div class="card-body">
|
|
7
|
+
<h2 class="card-title">title</h2>
|
|
8
|
+
body
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
The title is escaped and left out when there is none; the body is markup.
|
|
13
|
+
|
|
14
|
+
Modifiers, each a name from a closed set that maps to one DaisyUI class:
|
|
15
|
+
|
|
16
|
+
size xs, sm, md, lg, xl
|
|
17
|
+
style border, dash
|
|
18
|
+
|
|
19
|
+
A value outside its set is a render error.
|
|
20
|
+
|
|
21
|
+
`{% call cardActions() %}…{% endcall %}`, inside a card's body, is the row
|
|
22
|
+
of buttons at its foot: `<div class="card-actions justify-end">`. #}
|
|
23
|
+
|
|
24
|
+
{% set SIZES = { xs: 'card-xs', sm: 'card-sm', md: 'card-md', lg: 'card-lg', xl: 'card-xl' } %}
|
|
25
|
+
{% set STYLES = { border: 'card-border', dash: 'card-dash' } %}
|
|
26
|
+
|
|
27
|
+
{% macro card(title='', size=none, style=none) -%}
|
|
28
|
+
<div class="card bg-base-100 shadow-sm{{ SIZES | modifier(size) }}{{ STYLES | modifier(style) }}">
|
|
29
|
+
<div class="card-body">
|
|
30
|
+
{% if title %}
|
|
31
|
+
<h2 class="card-title">{{ title }}</h2>
|
|
32
|
+
{% endif %}
|
|
33
|
+
{{ caller() }}
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
{%- endmacro %}
|
|
37
|
+
|
|
38
|
+
{% macro cardActions() -%}
|
|
39
|
+
<div class="card-actions justify-end">{{ caller() }}</div>
|
|
40
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{# A collapse: a heading that shows and hides what is under it.
|
|
2
|
+
|
|
3
|
+
`{% call collapse(title, …) %}body{% endcall %}` emits
|
|
4
|
+
|
|
5
|
+
<details class="collapse collapse-arrow border border-base-300 bg-base-100">
|
|
6
|
+
<summary class="collapse-title font-semibold">title</summary>
|
|
7
|
+
<div class="collapse-content">body</div>
|
|
8
|
+
</details>
|
|
9
|
+
|
|
10
|
+
`open` starts it open. The title is escaped; the body is markup.
|
|
11
|
+
`fields` wraps the body in a `<fieldset>` whose `sr-only` legend repeats
|
|
12
|
+
the title.
|
|
13
|
+
|
|
14
|
+
Modifiers:
|
|
15
|
+
|
|
16
|
+
icon arrow (the default), plus
|
|
17
|
+
|
|
18
|
+
A value outside its set is a render error. #}
|
|
19
|
+
|
|
20
|
+
{% set ICONS = { arrow: 'collapse-arrow', plus: 'collapse-plus' } %}
|
|
21
|
+
|
|
22
|
+
{% macro collapse(title, open=false, fields=false, icon='arrow') -%}
|
|
23
|
+
<details class="collapse{{ ICONS | modifier(icon) }} border border-base-300 bg-base-100"{% if open %} open{% endif %}>
|
|
24
|
+
<summary class="collapse-title font-semibold">{{ title }}</summary>
|
|
25
|
+
<div class="collapse-content">
|
|
26
|
+
{% if fields %}
|
|
27
|
+
<fieldset class="flex min-w-0 flex-col gap-1">
|
|
28
|
+
<legend class="sr-only">{{ title }}</legend>
|
|
29
|
+
{{ caller() }}
|
|
30
|
+
</fieldset>
|
|
31
|
+
{% else %}
|
|
32
|
+
{{ caller() }}
|
|
33
|
+
{% endif %}
|
|
34
|
+
</div>
|
|
35
|
+
</details>
|
|
36
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{# A value to copy: a readonly box, and a button that copies what it holds.
|
|
2
|
+
|
|
3
|
+
`copyField(id, label, value)` emits
|
|
4
|
+
|
|
5
|
+
<label class="sr-only" for="id">label</label>
|
|
6
|
+
<div class="join w-full">
|
|
7
|
+
<input id="id" type="text" value="value" readonly class="input input-sm join-item w-full" />
|
|
8
|
+
<button type="button" class="btn btn-sm join-item" data-copy="id" hidden>Copy</button>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
The button stays `hidden` until `static/copy.js` reveals it. Label and
|
|
12
|
+
value are escaped. #}
|
|
13
|
+
|
|
14
|
+
{% macro copyField(id, label, value) -%}
|
|
15
|
+
<label class="sr-only" for="{{ id }}">{{ label }}</label>
|
|
16
|
+
<div class="join w-full">
|
|
17
|
+
<input id="{{ id }}" type="text" value="{{ value }}" readonly class="input input-sm join-item w-full min-w-48" />
|
|
18
|
+
<button type="button" class="btn btn-sm join-item" data-copy="{{ id }}" hidden>Copy</button>
|
|
19
|
+
</div>
|
|
20
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{# A dropdown: a button that opens its body as a popover.
|
|
2
|
+
|
|
3
|
+
`{% call dropdown(id, label, …) %}body{% endcall %}` emits
|
|
4
|
+
|
|
5
|
+
<button type="button" class="btn btn-sm" popovertarget="id">label</button>
|
|
6
|
+
<div class="dropdown rounded-box bg-base-100 z-1 w-52 shadow-sm" popover id="id">
|
|
7
|
+
body
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
`id` must be unique on the page. The body is markup. The label is escaped.
|
|
11
|
+
|
|
12
|
+
The trigger is `button` from `components/button.njk` and takes its
|
|
13
|
+
`color`, `style` and `size` (sm by default). `placement` is start, center,
|
|
14
|
+
end, top, bottom, left or right, each mapping to its `dropdown-*` class. A
|
|
15
|
+
value outside its set is a render error. #}
|
|
16
|
+
|
|
17
|
+
{% from "components/button.njk" import button %}
|
|
18
|
+
|
|
19
|
+
{% set PLACEMENTS = {
|
|
20
|
+
start: 'dropdown-start',
|
|
21
|
+
center: 'dropdown-center',
|
|
22
|
+
end: 'dropdown-end',
|
|
23
|
+
top: 'dropdown-top',
|
|
24
|
+
bottom: 'dropdown-bottom',
|
|
25
|
+
left: 'dropdown-left',
|
|
26
|
+
right: 'dropdown-right'
|
|
27
|
+
} %}
|
|
28
|
+
|
|
29
|
+
{% macro dropdown(id, label, color=none, style=none, size='sm', placement=none) -%}
|
|
30
|
+
{{ button(label, color=color, style=style, size=size, popovertarget=id) }}
|
|
31
|
+
<div class="dropdown rounded-box bg-base-100 z-1 w-52 shadow-sm{{ PLACEMENTS | modifier(placement) }}" popover id="{{ id }}">{{ caller() }}</div>
|
|
32
|
+
{%- endmacro %}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{# One labelled box, written once.
|
|
1
|
+
{# One labelled box, written once, in DaisyUI's form components.
|
|
2
2
|
|
|
3
3
|
Every admin form used to spell out its own `<label for>`, control, hint and
|
|
4
4
|
error, roughly seventy-five times across sixteen templates, against a class
|
|
@@ -7,21 +7,34 @@
|
|
|
7
7
|
forget the error line, leave a label unbound, or invent a class the
|
|
8
8
|
stylesheet has never heard of, and nothing says it is wrong.
|
|
9
9
|
|
|
10
|
-
Each macro here emits the same four things in the
|
|
10
|
+
Each macro here emits one `fieldset` holding the same four things in the
|
|
11
|
+
same order:
|
|
11
12
|
|
|
12
|
-
1. the `<label for>`, bound to the control by its id,
|
|
13
|
-
|
|
13
|
+
1. the `<label for>`, bound to the control by its id, drawn as the
|
|
14
|
+
field's `fieldset-legend`,
|
|
15
|
+
2. the control: `input`, `textarea`, `select` or `checkbox`, small, the
|
|
16
|
+
width of its column, and a `validator` when the form refused it,
|
|
14
17
|
3. the field error, where the form has something to say about this box,
|
|
15
|
-
|
|
18
|
+
as the `validator-hint` that a refused control turns red,
|
|
19
|
+
4. the hint, where the field needs explaining, as a `label`.
|
|
20
|
+
|
|
21
|
+
DaisyUI's `fieldset` and `fieldset-legend` are classes on a `<div>` and a
|
|
22
|
+
`<label>` here, not a `<fieldset>` and `<legend>`, which name a group
|
|
23
|
+
rather than one control.
|
|
16
24
|
|
|
17
25
|
Error before hint, because that is the order every screen already used: the
|
|
18
26
|
sentence about what went wrong belongs next to the box it is about, and the
|
|
19
27
|
standing explanation sits under both.
|
|
20
28
|
|
|
21
|
-
This file is under the
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
This file is under the admin directory, which is the admin environment's
|
|
30
|
+
only search root (`src/admin/templates.ts`). A site's theme can override
|
|
31
|
+
any public template and cannot reach in here, so no theme can shadow or
|
|
32
|
+
break a field on the login form.
|
|
33
|
+
|
|
34
|
+
`hintId` gives the hint an id and names it in the control's
|
|
35
|
+
`aria-describedby`, after the error where there is one, so a screen reader
|
|
36
|
+
reads the explanation with the box rather than leaving it to be found. A
|
|
37
|
+
textarea given no `name` only shows something and posts nothing.
|
|
25
38
|
|
|
26
39
|
`label` and `hint` are written into the page as markup, because the admin's
|
|
27
40
|
labels and hints carry `<code>`, `<em>` and links. They are template
|
|
@@ -30,19 +43,25 @@
|
|
|
30
43
|
interpolates before this ever sees it. `error` is a message from the server
|
|
31
44
|
and is escaped. #}
|
|
32
45
|
|
|
46
|
+
{% from "components/alert.njk" import alert %}
|
|
47
|
+
|
|
33
48
|
{# The field error, then the hint: the two paragraphs every macro below ends
|
|
34
|
-
with.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
with. The error is a `validator-hint`, which DaisyUI shows through a
|
|
50
|
+
sibling combinator after a `.validator[aria-invalid]`, so it has to stay
|
|
51
|
+
that control's later sibling. `errorHtml` is the rare second thing a field
|
|
52
|
+
has to say — the Reading page's "that page is no longer published" —
|
|
53
|
+
already written as its own paragraph, and drawn in the error colour whether
|
|
54
|
+
or not the form was refused. The hint is a `label` made `block` and
|
|
55
|
+
`whitespace-normal`: DaisyUI's is `inline-flex` and `nowrap`. #}
|
|
56
|
+
{% macro _below(id, hint, error, errorHtml, hintId='') %}
|
|
38
57
|
{% if error %}
|
|
39
|
-
<p class="
|
|
58
|
+
<p class="validator-hint" id="{{ id }}-error">{{ error }}</p>
|
|
40
59
|
{% endif %}
|
|
41
60
|
{% if errorHtml %}
|
|
42
|
-
{{ errorHtml | safe }}
|
|
61
|
+
<div class="text-error">{{ errorHtml | safe }}</div>
|
|
43
62
|
{% endif %}
|
|
44
63
|
{% if hint %}
|
|
45
|
-
<p class="
|
|
64
|
+
<p class="label block whitespace-normal"{% if hintId %} id="{{ hintId }}"{% endif %}>{{ hint | safe }}</p>
|
|
46
65
|
{% endif %}
|
|
47
66
|
{% endmacro %}
|
|
48
67
|
|
|
@@ -50,8 +69,8 @@
|
|
|
50
69
|
invalid, and which paragraph says why, so a screen reader reads the message
|
|
51
70
|
with the field rather than leaving it to be found. `errorHtml` is not named
|
|
52
71
|
here: it is a standing note about the stored value, not a refusal. #}
|
|
53
|
-
{% macro _invalid(id, error) -%}
|
|
54
|
-
{% if error %} aria-invalid="true" aria-describedby="{{ id }}-error"{% endif %}
|
|
72
|
+
{% macro _invalid(id, error, hintId='') -%}
|
|
73
|
+
{% if error %} aria-invalid="true" aria-describedby="{{ id }}-error{% if hintId %} {{ hintId }}{% endif %}"{% elif hintId %} aria-describedby="{{ hintId }}"{% endif %}
|
|
55
74
|
{%- endmacro %}
|
|
56
75
|
|
|
57
76
|
{# A box somebody types into: text, email, number, or whatever `type` says.
|
|
@@ -67,20 +86,25 @@
|
|
|
67
86
|
hint='',
|
|
68
87
|
error='',
|
|
69
88
|
errorHtml='',
|
|
89
|
+
hintId='',
|
|
70
90
|
min='',
|
|
71
91
|
step='',
|
|
72
92
|
inputmode='',
|
|
73
93
|
autocomplete='',
|
|
74
94
|
placeholder='',
|
|
95
|
+
list='',
|
|
96
|
+
maxlength='',
|
|
75
97
|
spellcheck='',
|
|
76
98
|
required=false,
|
|
77
99
|
autofocus=false,
|
|
78
100
|
readonly=false,
|
|
79
101
|
disabled=false
|
|
80
102
|
) %}
|
|
81
|
-
|
|
82
|
-
<
|
|
83
|
-
{{
|
|
103
|
+
<div class="fieldset">
|
|
104
|
+
<label class="fieldset-legend" for="{{ id }}">{{ label | safe }}</label>
|
|
105
|
+
<input id="{{ id }}" name="{{ name }}" type="{{ type }}"{% if min %} min="{{ min }}"{% endif %}{% if step %} step="{{ step }}"{% endif %}{% if inputmode %} inputmode="{{ inputmode }}"{% endif %}{% if autocomplete %} autocomplete="{{ autocomplete }}"{% endif %} value="{{ value }}"{% if list %} list="{{ list }}"{% endif %}{% if maxlength %} maxlength="{{ maxlength }}"{% endif %}{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if readonly %} readonly{% endif %}{% if disabled %} disabled{% endif %}{{ _invalid(id, error, hintId) }} class="input input-sm w-full read-only:bg-base-200 read-only:border-dashed{% if error %} validator{% endif %}" />
|
|
106
|
+
{{ _below(id, hint, error, errorHtml, hintId) }}
|
|
107
|
+
</div>
|
|
84
108
|
{%- endmacro %}
|
|
85
109
|
|
|
86
110
|
{# A password box. It carries `value=""` and no way to set one: nothing typed
|
|
@@ -100,9 +124,11 @@
|
|
|
100
124
|
required=false,
|
|
101
125
|
autofocus=false
|
|
102
126
|
) %}
|
|
103
|
-
|
|
104
|
-
<
|
|
127
|
+
<div class="fieldset">
|
|
128
|
+
<label class="fieldset-legend" for="{{ id }}">{{ label | safe }}</label>
|
|
129
|
+
<input id="{{ id }}" name="{{ name }}" type="password" value=""{% if autocomplete %} autocomplete="{{ autocomplete }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{{ _invalid(id, error) }} class="input input-sm w-full{% if error %} validator{% endif %}" />
|
|
105
130
|
{{ _below(id, hint, error, errorHtml) }}
|
|
131
|
+
</div>
|
|
106
132
|
{%- endmacro %}
|
|
107
133
|
|
|
108
134
|
{# A box of several lines. #}
|
|
@@ -114,6 +140,7 @@
|
|
|
114
140
|
hint='',
|
|
115
141
|
error='',
|
|
116
142
|
errorHtml='',
|
|
143
|
+
hintId='',
|
|
117
144
|
rows='',
|
|
118
145
|
spellcheck='',
|
|
119
146
|
required=false,
|
|
@@ -121,9 +148,11 @@
|
|
|
121
148
|
readonly=false,
|
|
122
149
|
disabled=false
|
|
123
150
|
) %}
|
|
124
|
-
|
|
125
|
-
<
|
|
126
|
-
{{
|
|
151
|
+
<div class="fieldset">
|
|
152
|
+
<label class="fieldset-legend" for="{{ id }}">{{ label | safe }}</label>
|
|
153
|
+
<textarea id="{{ id }}"{% if name %} name="{{ name }}"{% endif %}{% if rows %} rows="{{ rows }}"{% endif %}{% if spellcheck %} spellcheck="{{ spellcheck }}"{% endif %}{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if readonly %} readonly{% endif %}{% if disabled %} disabled{% endif %}{{ _invalid(id, error, hintId) }} class="textarea textarea-sm w-full read-only:bg-base-200 read-only:border-dashed{% if error %} validator{% endif %}">{{ value }}</textarea>
|
|
154
|
+
{{ _below(id, hint, error, errorHtml, hintId) }}
|
|
155
|
+
</div>
|
|
127
156
|
{%- endmacro %}
|
|
128
157
|
|
|
129
158
|
{# A list to pick from. The options are the call's body, because which of them
|
|
@@ -140,20 +169,22 @@
|
|
|
140
169
|
hint='',
|
|
141
170
|
error='',
|
|
142
171
|
errorHtml='',
|
|
172
|
+
hintId='',
|
|
143
173
|
required=false,
|
|
144
174
|
autofocus=false,
|
|
145
175
|
disabled=false
|
|
146
176
|
) %}
|
|
147
|
-
|
|
148
|
-
<
|
|
177
|
+
<div class="fieldset">
|
|
178
|
+
<label class="fieldset-legend" for="{{ id }}">{{ label | safe }}</label>
|
|
179
|
+
<select id="{{ id }}" name="{{ name }}"{% if required %} required{% endif %}{% if autofocus %} autofocus{% endif %}{% if disabled %} disabled{% endif %}{{ _invalid(id, error, hintId) }} class="select select-sm w-full{% if error %} validator{% endif %}">
|
|
149
180
|
{{ caller() }}
|
|
150
181
|
</select>
|
|
151
|
-
{{ _below(id, hint, error, errorHtml) }}
|
|
182
|
+
{{ _below(id, hint, error, errorHtml, hintId) }}
|
|
183
|
+
</div>
|
|
152
184
|
{%- endmacro %}
|
|
153
185
|
|
|
154
|
-
{# A switch
|
|
155
|
-
|
|
156
|
-
stylesheet lays that pair out with. #}
|
|
186
|
+
{# A switch: the tick inside its label. A refused box puts `validator` on
|
|
187
|
+
the label, since the `validator-hint` must be its later sibling. #}
|
|
157
188
|
{% macro checkbox(
|
|
158
189
|
id,
|
|
159
190
|
name,
|
|
@@ -163,20 +194,23 @@
|
|
|
163
194
|
hint='',
|
|
164
195
|
error='',
|
|
165
196
|
errorHtml='',
|
|
197
|
+
hintId='',
|
|
166
198
|
disabled=false
|
|
167
199
|
) %}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
<
|
|
171
|
-
|
|
172
|
-
|
|
200
|
+
<div class="fieldset">
|
|
201
|
+
<label class="label whitespace-normal text-base-content{% if error %} validator{% endif %}" for="{{ id }}">
|
|
202
|
+
<input id="{{ id }}" name="{{ name }}" type="checkbox" value="{{ value }}"{% if checked %} checked{% endif %}{% if disabled %} disabled{% endif %}{{ _invalid(id, error, hintId) }} class="checkbox checkbox-sm" />
|
|
203
|
+
{{ label | safe }}
|
|
204
|
+
</label>
|
|
205
|
+
{{ _below(id, hint, error, errorHtml, hintId) }}
|
|
206
|
+
</div>
|
|
173
207
|
{%- endmacro %}
|
|
174
208
|
|
|
175
209
|
{# The top of a refused form: what went wrong, and a link to every field that
|
|
176
|
-
needs putting right. It is an alert and takes focus when the page
|
|
177
|
-
a screen reader hears it at once and a keyboard starts from it,
|
|
178
|
-
script. It comes before the form, so its `autofocus` is the first
|
|
179
|
-
page and wins over any a field carries.
|
|
210
|
+
needs putting right. It is an error alert and takes focus when the page
|
|
211
|
+
loads, so a screen reader hears it at once and a keyboard starts from it,
|
|
212
|
+
with no script. It comes before the form, so its `autofocus` is the first
|
|
213
|
+
on the page and wins over any a field carries.
|
|
180
214
|
|
|
181
215
|
A form with one thing to say calls it bare, with the message as the heading.
|
|
182
216
|
A form with fields to fix calls it with a `problem` per field; one given no
|
|
@@ -190,19 +224,21 @@
|
|
|
190
224
|
fixed. #}
|
|
191
225
|
{% macro summary(heading) %}
|
|
192
226
|
{% set problems = (caller() if caller else '') | trim %}
|
|
193
|
-
|
|
194
|
-
|
|
227
|
+
{% call alert(color='error', labelledby='admin-error-summary-heading', focus=true) %}
|
|
228
|
+
<div>
|
|
229
|
+
<h2 id="admin-error-summary-heading" class="font-semibold">{{ heading }}</h2>
|
|
195
230
|
{% if problems %}
|
|
196
|
-
<ul>
|
|
231
|
+
<ul class="list-disc ps-5">
|
|
197
232
|
{{ problems | safe }}
|
|
198
233
|
</ul>
|
|
199
234
|
{% endif %}
|
|
200
235
|
</div>
|
|
236
|
+
{% endcall %}
|
|
201
237
|
{%- endmacro %}
|
|
202
238
|
|
|
203
239
|
{# One field's line in the summary: its message, linking to the field. #}
|
|
204
240
|
{% macro problem(id, message) %}
|
|
205
241
|
{% if message %}
|
|
206
|
-
<li><a href="#{{ id }}">{{ message }}</a></li>
|
|
242
|
+
<li><a class="link" href="#{{ id }}">{{ message }}</a></li>
|
|
207
243
|
{% endif %}
|
|
208
244
|
{%- endmacro %}
|