Dashboard

DangerZone

A red-bordered block of destructive actions (DangerAction), each a form, link or callback that asks before it acts.

#dashboard#settings#security#button#forms#no-js

Demo

Preview

Danger zone

  • Transfer ownership

    Give this project to someone else on the team.

    Transfer
  • Archive project

    Read-only for everyone; you can bring it back.

  • Delete project

    Everything in it goes, for good, within the hour.

Account

These act on your whole account.

  • Sign out everywhere

    Every other device is signed out.

  • Delete account

    Your projects, files and billing history go, for good.

Each button asks first; yes calls on_confirm.

Get it

It needs theme.qwe, as every component does, and button.qwe and confirm_button.qwe; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/button.qwe$ curl -O https://quillolang.org/components/ui/confirm_button.qwe$ curl -O https://quillolang.org/components/ui/danger_zone.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/danger_zone.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/danger_zone.qwe
component DangerZoneDemoLive() {    state done = null    <div.demo-stack>        <DangerZone title="Account" description="These act on your whole account.">            <DangerAction title="Sign out everywhere" text="Every other device is signed out." label="Sign out all"                          tone="neutral" on_confirm={() => { done = "Signed out everywhere." }}/>            <DangerAction title="Delete account" text="Your projects, files and billing history go, for good."                          on_confirm={() => { done = "Account deleted (not really)." }}/>        </DangerZone>        <p.demo-note>{done ?? "Each button asks first; yes calls on_confirm."}</p>    </div>}view DangerZoneDemo() =    <div.demo-stack>        <DangerZone id="danger-zone-demo">            <DangerAction title="Transfer ownership" text="Give this project to someone else on the team." label="Transfer"                          href="#danger_zone" tone="neutral" confirm={false}/>            <DangerAction title="Archive project" text="Read-only for everyone; you can bring it back." label="Archive"                          action="#danger_zone" name="archive" value="yes" tone="neutral" message="People lose write access until you restore it."/>            <DangerAction title="Delete project" text="Everything in it goes, for good, within the hour." label="Delete project"                          action="#danger_zone" name="delete" value="yes" message="The files, the database and the deploys go too."/>        </DangerZone>        <DangerZoneDemoLive/>    </div>

Source

ui/danger_zone.qwe
// DangerZone: the destructive actions of a settings page in a red-bordered// block, each with what it does and a button that asks before doing it.// DangerAction is one row. It uses ConfirmButton (ui/confirm_button.qwe) and// the Button styles.////   <DangerZone>//       <DangerAction title="Transfer ownership" text="Give this project to someone else." label="Transfer"//                     href="/project/transfer" tone="neutral" confirm={false}/>//       <DangerAction title="Delete project" text="Everything in it goes, for good." label="Delete project"//                     action="/project/delete" message="Type nothing; just be sure: the files go too."/>//   </DangerZone>//// DangerZone: title ("Danger zone"), description, children (DangerActions).// DangerAction: title and text, what it does; label, the button's text (the// title by default). action: the row is a <form method="post"> to that address,// sent with name and value (if given) when confirmed. href: a link instead.// on_confirm(): inside one of your components, what yes does instead.// message: the question asked first (a sentence about the title by default);// ok: the dialog's yes button (the label). tone: "danger" (default) or// "neutral", the button's. confirm: false skips the question (for a link that// leads to a page that asks).import ui/button.qwe use Buttonimport ui/confirm_button.qwe use ConfirmButtonimport ui/theme.qwe use ui_clslet danger_zone_css = css`.ui-danger { border: 1px solid color-mix(in srgb, var(--ui-danger) 45%, var(--ui-border)); border-radius: var(--ui-radius);  background: var(--ui-surface); font: 14px/1.5 var(--ui-font); color: var(--ui-fg); }.ui-danger-head { padding: 16px 20px 12px; border-bottom: 1px solid color-mix(in srgb, var(--ui-danger) 30%, var(--ui-border)); }.ui-danger-title { display: flex; align-items: center; gap: 8px; margin: 0; color: var(--ui-danger); font-size: 16px; font-weight: 600; line-height: 1.3; }.ui-danger-title svg { width: 18px; height: 18px; }.ui-danger-desc { margin: 3px 0 0; color: var(--ui-muted); }.ui-danger-rows { margin: 0; padding: 0; list-style: none; }.ui-danger-row + .ui-danger-row { border-top: 1px solid var(--ui-border); }.ui-danger-row > form, .ui-danger-row > div { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 24px; margin: 0; padding: 14px 20px; }.ui-danger-what { flex: 1 1 260px; min-width: 0; }.ui-danger-row-title { margin: 0; font-weight: 600; }.ui-danger-text { margin: 2px 0 0; color: var(--ui-muted); }`view DangerZone(title: str = "Danger zone", description: str? = null, id: str? = null, class: str? = null, children) {    style global danger_zone_css    let tid = id != null ? `${id}-title` : null    <section id={id} class={ui_cls(["ui-danger", class])} aria-labelledby={tid} aria-label={tid == null ? title : null}>        <div.ui-danger-head>            <h2.ui-danger-title id={tid}>                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"                     aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>                {title}            </h2>            {description != null && <p.ui-danger-desc>{description}</p>}        </div>        <ul.ui-danger-rows>{children}</ul>    </section>}view DangerAction(title: str, text: str? = null, label: str? = null, action: str? = null, href: str? = null, name: str? = null,                  value: str? = null, message: str? = null, ok: str? = null, tone: str = "danger", confirm: bool = true,                  on_confirm = null, class: str? = null) {    style global danger_zone_css    let word = label ?? title    let what = <div.ui-danger-what>        <p.ui-danger-row-title>{title}</p>        {text != null && <p.ui-danger-text>{text}</p>}    </div>    let button = confirm        ? <ConfirmButton message={message ?? `${title}: this cannot be undone.`} title={`${title}?`} ok={ok ?? word} tone={tone}                         href={href} name={name} value={value} variant="outline" size="sm" on_confirm={on_confirm}>{word}</ConfirmButton>        : <Button href={href} type={action != null ? "submit" : "button"} name={name} value={value} tone={tone} variant="outline" size="sm"                  on_click={on_confirm}>{word}</Button>    <li class={ui_cls(["ui-danger-row", class])}>        {action != null && on_confirm == null            ? <form method="post" action={action}>{what}{button}</form>            : <div>{what}{button}</div>}    </li>}
Thread
68Useful? Like it, or add to its thread below.

Thread 0 posts

On DangerZone: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.