# Modal

A window over the page with a title, a × and any content; the page behind is dimmed, blocked and does not scroll.
Esc, the × and a click on the dim background close it (the background click can be switched off); buttons in a footer
close it too. The focus stays inside while it is open and goes back to the element that opened it. The page opens and
closes it and is told **how** it was closed. Demo: `/modal` (https://components.hybriel.worldapi.org/modal).

Pure Hybriel: one file, `modal.hl`, with its own `Style`; no JavaScript file. It is the browser's own `<dialog>` opened
with `showModal()` (page behind inert, focus in, focus back), plus Tab / Shift+Tab going round inside, the reasons, the
background click and the scroll lock.

## Use it in an app

Copy `components/modal/modal.hl` (optionally `shared/tokens.hl` for the app's own accent), then:

```
import Modal from './modal.hl'

String result = ''

View {
	page { on close(e) { emit pageClosed(e) }
		button { type = "button"  "Delete…"  on click(e) { emit pageAsk(e) } }
		Modal { id = "confirm"  title = "Delete the draft?"
			p { "This cannot be undone." }
			footer {
				button { type = "button"  value = "cancel"  "Cancel" }
				button { type = "button"  value = "ok"  "Delete" }
			}
		}
	}
}

on pageAsk(e) { document.getElementById('confirm').showModal() }
on pageClosed(e) { if (e.detail.id == 'confirm' && e.detail.reason == 'ok') { result = 'deleted' } }
```

## Attributes
| Attribute | Meaning |
|---|---|
| `id` | the id of the `<dialog>`: the page opens / closes it by this id (required when there are several) — default `modal` |
| `title` | the heading of the window (also its `aria-label`) |
| `backdropClose` | `"false"` = a click on the dim background does NOT close it (default `"true"`) |
| `closeLabel` | the tooltip / label of the × (default `Close`) |
| content | everything written inside `Modal { … }` is the content (the host's content: its handlers and members are the page's) |

Write every attribute as a **literal** (`title = "…"`): then several modals stand on one page, each with its own values.
(A composed component's members are the page's state; bound to a changing member, all modals would share it.)

## Footer buttons
A `footer { … }` inside the content is the footer: laid out at the bottom right, sticky under a long content, the last
button is the main one (accent colour; black without a theme). A click on a footer button closes the modal with the button's `value` as the
reason (no `value` → its text). A footer button may have its own `on click` (the page's handler): it runs first, and when
it closes the modal itself with `close('saved')`, that reason wins (the demo's "Save"). A button that must keep the modal
open (e.g. a check that fails) goes in the content, not in the `footer`.

## Open and close from the page
- open: `document.getElementById('<id>').showModal()` in any handler (the focused element — usually the button that was
  clicked — gets the focus back when it closes).
- close: `document.getElementById('<id>').close('<reason>')` — the reason is what the page is told.

## Events
When it closes, a **bubbling `close` event** is sent from `<modal-dialog>`; catch it on any element around the modal:
`on close(e) { … }`.

| `e.detail.reason` | how it was closed |
|---|---|
| `close` | the × |
| `escape` | the Esc key |
| `backdrop` | a click on the dim background |
| the button's `value` (or text) | a footer button |
| whatever the page passed | `close('<reason>')` from the page |

`e.detail.id` is the modal's id. (A custom event name such as `modalclose` would be nicer, but hl:web binds only
standard DOM event names — hybriel ticket #31 — so it is `close`.)

## Behaviour
- The page behind is inert (no clicks, no focus) and does not scroll (`html { overflow: hidden }` while one is open).
- The window stays inside the screen (at most the screen height minus 2rem); a long content scrolls inside it.
- A press that starts inside the window and ends on the background (selecting text) does not close it.
- Phone: the window is the screen width minus 1rem each side.
- Colours are the semantic tokens with a black/white/grey fallback (the base look is black, white and greys (no frames; root README "Look"); an app that declares the WorldAPI tokens (layouts.worldapi.org's theme) restyles it): a white window on the grey dimmed page; buttons have no border.

## Not yet
Open / close animation; stacking one modal on top of another is untested; the page cannot bind the modal's `open` state
as a member (it opens it with `showModal()`).
