web using penguin-ui

This commit is contained in:
Priec
2026-08-07 21:37:40 +02:00
parent 7cda7ea833
commit c47e236713
8 changed files with 183 additions and 120 deletions

View File

@@ -1,54 +1,52 @@
{#
Blocking modal dialog — Penguin UI's "modal" component
(https://www.penguinui.com/components/modal, modern style), with the dark
variants dropped because the app has no dark theme.
Blocking failure modal — Penguin UI's danger modal, taken from
penguinui-components/modal/modal-alerts.html.
It is Tailwind + Alpine markup, so a page that calls this macro must load
both in its `head` block; `pages/add_table/add_table.html` shows the set.
The `@theme` block there maps Penguin UI's semantic colour names onto the
palette in `static/app.css`.
Same deal as ui/alert.html: the component's copy lives in its markup, so this
is the library file's danger variant with its text nodes replaced and nothing
else changed. Two structural edits, both because the server, not a click,
decides when this appears:
Rendered already open (`modalIsOpen: true`): the server only sends the
dialog when there is something the user has to be told, so there is no
trigger button — the response itself is the trigger.
- the library's trigger button is dropped, and `dangerModalIsOpen` starts
`true`. The server only sends the dialog when there is something the user
has to be told, so the response itself is the trigger.
- the footer button dismisses instead of acting ("Back to the form"): a
failure has nothing to confirm.
It is Tailwind + Alpine markup; x-trap needs the focus plugin, which
ui/base.html loads before Alpine itself.
{% import "ui/dialog.html" as dialog %}
{% call dialog::error("Could not add the column", message) %}{% endcall %}
#}
{% macro error(title, message) %}
<div x-data="{ modalIsOpen: true }">
<div x-cloak x-show="modalIsOpen" x-transition.opacity.duration.200ms
x-trap.inert.noscroll="modalIsOpen"
x-on:keydown.esc.window="modalIsOpen = false" x-on:click.self="modalIsOpen = false"
class="fixed inset-0 z-30 flex items-end justify-center bg-surfaceDark/20 p-4 pb-8 backdrop-blur-xs sm:items-center lg:p-8"
role="dialog" aria-modal="true" aria-labelledby="dialogTitle">
<!-- Modal Dialog -->
<div x-show="modalIsOpen"
x-transition:enter="transition delay-100 duration-200 ease-out motion-reduce:transition-opacity"
x-transition:enter-start="scale-50 opacity-0" x-transition:enter-end="scale-100 opacity-100"
class="flex max-w-lg flex-col gap-4 overflow-hidden rounded-lg border border-outline bg-surface text-onSurface">
<!-- Dialog Header -->
<div class="flex items-center justify-between border-b border-outline bg-surfaceAlt/60 p-4">
<h3 id="dialogTitle" class="font-semibold tracking-wide text-danger">{{ title }}</h3>
<button type="button" x-on:click="modalIsOpen = false" class="cursor-pointer text-onSurface" aria-label="close dialog">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor"
fill="none" stroke-width="1.4" class="h-5 w-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Dialog Body -->
<div class="px-4 py-4">
<p class="text-pretty whitespace-pre-wrap">{{ message }}</p>
</div>
<!-- Dialog Footer -->
<div class="flex flex-col-reverse justify-between gap-2 border-t border-outline bg-surfaceAlt/60 p-4 sm:flex-row sm:items-center md:justify-end">
<button type="button" x-on:click="modalIsOpen = false"
class="cursor-pointer rounded-md whitespace-nowrap bg-primary px-4 py-2 text-center text-sm font-medium tracking-wide text-onPrimary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:opacity-100 active:outline-offset-0">
Back to the form
</button>
</div>
<div x-data="{ dangerModalIsOpen: true }">
<div x-cloak x-show="dangerModalIsOpen" x-transition.opacity.duration.200ms x-trap.inert.noscroll="dangerModalIsOpen" x-on:keydown.esc.window="dangerModalIsOpen = false" x-on:click.self="dangerModalIsOpen = false" class="fixed inset-0 z-30 flex items-end justify-center bg-black/20 p-4 pb-8 backdrop-blur-md sm:items-center lg:p-8" role="dialog" aria-modal="true" aria-labelledby="dangerModalTitle">
<!-- Modal Dialog -->
<div x-show="dangerModalIsOpen" x-transition:enter="transition ease-out duration-200 delay-100 motion-reduce:transition-opacity" x-transition:enter-start="opacity-0 scale-50" x-transition:enter-end="opacity-100 scale-100" class="flex max-w-lg flex-col gap-4 overflow-hidden rounded-radius border border-outline bg-surface text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark">
<!-- Dialog Header -->
<div class="flex items-center justify-between border-b border-outline bg-surface-alt/60 px-4 py-2 dark:border-outline-dark dark:bg-surface-dark/20">
<div class="flex items-center justify-center rounded-full bg-danger/20 text-danger p-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-6" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd" />
</svg>
</div>
<button x-on:click="dangerModalIsOpen = false" aria-label="close modal">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="1.4" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<!-- Dialog Body -->
<div class="px-4 text-center">
<h3 id="dangerModalTitle" class="mb-2 font-semibold tracking-wide text-on-surface-strong dark:text-on-surface-dark-strong">{{ title }}</h3>
<p class="whitespace-pre-wrap">{{ message }}</p>
</div>
<!-- Dialog Footer -->
<div class="flex items-center justify-center border-outline p-4 dark:border-outline-dark">
<button x-on:click="dangerModalIsOpen = false" type="button" class="w-full whitespace-nowrap rounded-radius border border-danger bg-danger px-4 py-2 text-center text-sm font-semibold tracking-wide text-on-danger transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger active:opacity-100 active:outline-offset-0">Back to the form</button>
</div>
</div>
</div>
</div>
</div>
{% endmacro %}