components
All repositories: gitoria
4.2 KB
# ModalA 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 footerclose it too. The focus stays inside while it is open and goes back to the element that opened it. The page opens andcloses 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>` openedwith `showModal()` (page behind inert, focus in, focus back), plus Tab / Shift+Tab going round inside, the reasons, thebackground click and the scroll lock.## Use it in an appCopy `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 buttonsA `footer { … }` inside the content is the footer: laid out at the bottom right, sticky under a long content, the lastbutton is the main one (accent colour). A click on a footer button closes the modal with the button's `value` as thereason (no `value` → its text). A footer button may have its own `on click` (the page's handler): it runs first, and whenit closes the modal itself with `close('saved')`, that reason wins (the demo's "Save"). A button that must keep the modalopen (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 wasclicked — gets the focus back when it closes).- close: `document.getElementById('<id>').close('<reason>')` — the reason is what the page is told.## EventsWhen 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 onlystandard 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 the palette as fallback; buttons have no border.## Not yetOpen / close animation; stacking one modal on top of another is untested; the page cannot bind the modal's `open` stateas a member (it opens it with `showModal()`).
Branches
- mainmain branch