Basic Modal
A titled dialog with a focus trap, Escape to close and focus returned to the trigger.
6 個のフレームワーク中級
A dialog wrapped around a real form - Enter submits, submitting closes it.
<!--
A form in a dialog is still a form: the submit button is type="submit" inside a
real <form>, so Enter from any field submits and the browser's own required/
type validation runs before your handler does. Wiring the primary action as a
type="button" with an onclick is the common mistake here - it silently breaks
Enter-to-submit for everyone and validation for everyone.
Initial focus goes to the first FIELD, not the close button: the reason this
dialog opened is that the user intends to type.
-->
<button class="fmodal-trigger" type="button" data-fmodal-open>New project</button>
<div class="fmodal" id="form-modal" hidden>
<div class="fmodal__backdrop" data-fmodal-close></div>
<div class="fmodal__panel" role="dialog" aria-modal="true" aria-labelledby="form-modal-title">
<h2 class="fmodal__title" id="form-modal-title">New project</h2>
<form class="fmodal__form" id="form-modal-form">
<div class="fmodal__field">
<label class="fmodal__label" for="form-modal-name">Project name</label>
<input class="fmodal__input" id="form-modal-name" name="name" type="text" required placeholder="Orbit redesign" />
</div>
<div class="fmodal__field">
<label class="fmodal__label" for="form-modal-desc">Description</label>
<textarea class="fmodal__input" id="form-modal-desc" name="description" rows="3" placeholder="What is this for?"></textarea>
</div>
<div class="fmodal__actions">
<button class="fmodal__btn" type="button" data-fmodal-close>Cancel</button>
<button class="fmodal__btn fmodal__btn--primary" type="submit">Create</button>
</div>
</form>
</div>
</div>
<script>
(function () {
var FOCUSABLE =
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
var root = document.getElementById('form-modal');
var trigger = document.querySelector('[data-fmodal-open]');
var panel = root.querySelector('.fmodal__panel');
var form = document.getElementById('form-modal-form');
var firstField = document.getElementById('form-modal-name');
var restore = null;
function focusables() {
return Array.prototype.slice.call(panel.querySelectorAll(FOCUSABLE));
}
function open() {
restore = document.activeElement;
root.hidden = false;
document.body.style.overflow = 'hidden';
firstField.focus();
}
function close() {
root.hidden = true;
document.body.style.overflow = '';
if (restore && typeof restore.focus === 'function') restore.focus();
}
trigger.addEventListener('click', open);
Array.prototype.forEach.call(root.querySelectorAll('[data-fmodal-close]'), function (el) {
el.addEventListener('click', close);
});
form.addEventListener('submit', function (event) {
event.preventDefault();
// Submitting is a successful exit, so it closes and resets - a dialog
// reopened with the last submission still in it reads as a failure.
close();
form.reset();
});
root.addEventListener('keydown', function (event) {
if (event.key === 'Escape') {
close();
return;
}
if (event.key !== 'Tab') return;
var items = focusables();
if (!items.length) return;
var first = items[0];
var last = items[items.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
});
})();
</script>| Prop | 型 | デフォルト | 説明 |
|---|---|---|---|
open必須 | boolean | - | コンポーネントを表示するかどうか。 |
title必須 | string | - | カードの見出しテキスト。 |
ctaLabel | string | 'Create' | CTA ボタンのテキスト。 |
dismissLabel | string | 'Cancel' | 閉じるボタンのアクセシブルな名前。 |
onDismiss必須 | () => void | - | ユーザーが通知を閉じたときに呼ばれます。 |
onSubmit | (draft: ProjectDraft) => void | - | 送信時にフォームの値とともに呼ばれます。 |
className | string | - | ルート要素にマージされる追加クラス。 |
The primary action is a `type="submit"` inside a real `<form>`. Wiring it as a `type="button"` with an onClick is the usual mistake and it costs you two things silently: Enter from a field stops submitting, and the browser stops enforcing `required` before your handler runs. Initial focus goes to the first FIELD rather than the close button - the user opened this to type. On submit it resets and closes, because a dialog reopened with the last submission still in it reads as a failure. Everything else - trap, Escape, scroll lock, focus restore - matches `modal-basic`.